From faa59c4c1680b88948971ed00f667ef1260653b6 Mon Sep 17 00:00:00 2001 From: Iurii Kogan Date: Sun, 1 Feb 2026 11:43:26 +0000 Subject: [PATCH 1/4] fix: migrate Gemini client to google-genai SDK fix: mark gemini async test with pytest.mark.asyncio security: upgrade next to 16.1.5 in docs and visualizer to fix SNYK-JS-NEXT-15104645 --- .gemini/settings.json | 12 + .gitignore | 8 +- .tessl/.gitignore | 2 + .tessl/RULES.md | 7 + .../cli-setup/steering/query_library_docs.md | 28 + .tessl/tiles/tessl/cli-setup/tile.json | 11 + .../tessl/pypi-anthropic/docs/api/batches.md | 199 ++ .../pypi-anthropic/docs/api/completions.md | 285 ++ .../tessl/pypi-anthropic/docs/api/messages.md | 716 ++++ .../tessl/pypi-anthropic/docs/api/models.md | 161 + .../pypi-anthropic/docs/api/streaming.md | 838 +++++ .../tessl/pypi-anthropic/docs/api/tools.md | 423 +++ .../tessl/pypi-anthropic/docs/beta/batches.md | 611 ++++ .../tessl/pypi-anthropic/docs/beta/files.md | 669 ++++ .../tessl/pypi-anthropic/docs/beta/index.md | 358 ++ .../docs/beta/message-features.md | 1134 +++++++ .../tessl/pypi-anthropic/docs/beta/skills.md | 599 ++++ .../docs/common-tasks/basic-messaging.md | 336 ++ .../docs/common-tasks/multimodal-input.md | 421 +++ .../docs/common-tasks/streaming-responses.md | 450 +++ .../docs/common-tasks/tool-integration.md | 484 +++ .../docs/guides/batch-processing.md | 288 ++ .../docs/guides/error-handling.md | 324 ++ .../docs/guides/getting-started.md | 229 ++ .../pypi-anthropic/docs/guides/multimodal.md | 300 ++ .../docs/guides/streaming-guide.md | 221 ++ .../pypi-anthropic/docs/guides/tool-usage.md | 342 ++ .../tiles/tessl/pypi-anthropic/docs/index.md | 398 +++ .../pypi-anthropic/docs/platforms/bedrock.md | 148 + .../pypi-anthropic/docs/platforms/foundry.md | 144 + .../pypi-anthropic/docs/platforms/vertex.md | 157 + .../docs/quick-reference/batches.md | 157 + .../docs/quick-reference/messages.md | 97 + .../docs/quick-reference/models.md | 98 + .../docs/quick-reference/streaming.md | 115 + .../docs/quick-reference/tools.md | 159 + .../docs/reference/client-config.md | 305 ++ .../pypi-anthropic/docs/reference/errors.md | 287 ++ .../docs/reference/pagination.md | 615 ++++ .../pypi-anthropic/docs/reference/types.md | 1413 ++++++++ .../docs/reference/utilities.md | 169 + .tessl/tiles/tessl/pypi-anthropic/tile.json | 8 + .../tessl/pypi-dill/docs/configuration.md | 636 ++++ .../pypi-dill/docs/core-serialization.md | 285 ++ .../tessl/pypi-dill/docs/diagnostic-tools.md | 467 +++ .tessl/tiles/tessl/pypi-dill/docs/index.md | 453 +++ .../tessl/pypi-dill/docs/pickler-classes.md | 344 ++ .../pypi-dill/docs/session-management.md | 361 ++ .../tessl/pypi-dill/docs/source-analysis.md | 576 ++++ .../tessl/pypi-dill/docs/temp-operations.md | 705 ++++ .../tessl/pypi-dill/docs/type-registry.md | 397 +++ .tessl/tiles/tessl/pypi-dill/tile.json | 7 + .../tessl/pypi-google-genai/docs/batches.md | 408 +++ .../tessl/pypi-google-genai/docs/caching.md | 455 +++ .../tessl/pypi-google-genai/docs/chats.md | 738 ++++ .../tessl/pypi-google-genai/docs/client.md | 463 +++ .../docs/content-generation.md | 986 ++++++ .../pypi-google-genai/docs/embeddings.md | 346 ++ .../docs/file-search-stores.md | 520 +++ .../tessl/pypi-google-genai/docs/files.md | 450 +++ .../docs/image-generation.md | 551 +++ .../tessl/pypi-google-genai/docs/index.md | 599 ++++ .../tessl/pypi-google-genai/docs/live.md | 473 +++ .../tessl/pypi-google-genai/docs/models.md | 355 ++ .../pypi-google-genai/docs/operations.md | 290 ++ .../tessl/pypi-google-genai/docs/tokens.md | 328 ++ .../tessl/pypi-google-genai/docs/tuning.md | 405 +++ .../docs/video-generation.md | 319 ++ .../tiles/tessl/pypi-google-genai/tile.json | 8 + .../pypi-modal/docs/compute-resources.md | 490 +++ .../tessl/pypi-modal/docs/container-images.md | 503 +++ .../docs/core-application-client.md | 284 ++ .../docs/function-decorators-helpers.md | 418 +++ .tessl/tiles/tessl/pypi-modal/docs/index.md | 346 ++ .../docs/infrastructure-services.md | 389 +++ .../pypi-modal/docs/runtime-utilities.md | 626 ++++ .../pypi-modal/docs/scheduling-reliability.md | 456 +++ .../tessl/pypi-modal/docs/storage-data.md | 528 +++ .../tessl/pypi-modal/docs/utility-classes.md | 791 +++++ .../pypi-modal/docs/web-api-integration.md | 567 ++++ .tessl/tiles/tessl/pypi-modal/tile.json | 7 + .../tessl/pypi-openai/docs/assistants.md | 540 +++ .tessl/tiles/tessl/pypi-openai/docs/audio.md | 579 ++++ .../tiles/tessl/pypi-openai/docs/batches.md | 526 +++ .../pypi-openai/docs/chat-completions.md | 1018 ++++++ .../tiles/tessl/pypi-openai/docs/chatkit.md | 404 +++ .../pypi-openai/docs/client-initialization.md | 740 ++++ .../tessl/pypi-openai/docs/completions.md | 292 ++ .../tessl/pypi-openai/docs/containers.md | 641 ++++ .../tessl/pypi-openai/docs/conversations.md | 599 ++++ .../tessl/pypi-openai/docs/embeddings.md | 269 ++ .tessl/tiles/tessl/pypi-openai/docs/evals.md | 775 +++++ .tessl/tiles/tessl/pypi-openai/docs/files.md | 512 +++ .../tessl/pypi-openai/docs/fine-tuning.md | 845 +++++ .tessl/tiles/tessl/pypi-openai/docs/images.md | 617 ++++ .tessl/tiles/tessl/pypi-openai/docs/index.md | 2971 +++++++++++++++++ .tessl/tiles/tessl/pypi-openai/docs/models.md | 243 ++ .../tessl/pypi-openai/docs/moderations.md | 356 ++ .../tiles/tessl/pypi-openai/docs/realtime.md | 588 ++++ .../tiles/tessl/pypi-openai/docs/responses.md | 581 ++++ .tessl/tiles/tessl/pypi-openai/docs/runs.md | 725 ++++ .../pypi-openai/docs/threads-messages.md | 679 ++++ .../tiles/tessl/pypi-openai/docs/uploads.md | 466 +++ .../tessl/pypi-openai/docs/vector-stores.md | 1146 +++++++ .tessl/tiles/tessl/pypi-openai/docs/videos.md | 361 ++ .../tiles/tessl/pypi-openai/docs/webhooks.md | 503 +++ .tessl/tiles/tessl/pypi-openai/tile.json | 8 + .../pypi-portkey-ai/docs/administration.md | 27 + .../docs/assistants-threads.md | 71 + .../pypi-portkey-ai/docs/batch-processing.md | 27 + .../pypi-portkey-ai/docs/beta-realtime.md | 290 ++ .../pypi-portkey-ai/docs/chat-completions.md | 364 ++ .../docs/configuration-management.md | 27 + .../pypi-portkey-ai/docs/container-content.md | 20 + .../tessl/pypi-portkey-ai/docs/core-client.md | 274 ++ .../tessl/pypi-portkey-ai/docs/embeddings.md | 140 + .../docs/evaluation-testing.md | 22 + .../docs/feedback-collections.md | 19 + .../pypi-portkey-ai/docs/file-management.md | 45 + .../tessl/pypi-portkey-ai/docs/fine-tuning.md | 34 + .../docs/framework-integrations.md | 60 + .../tiles/tessl/pypi-portkey-ai/docs/index.md | 714 ++++ .../pypi-portkey-ai/docs/key-management.md | 21 + .../tessl/pypi-portkey-ai/docs/models.md | 206 ++ .../pypi-portkey-ai/docs/multimodal-apis.md | 68 + .../docs/observability-analytics.md | 528 +++ .../pypi-portkey-ai/docs/prompt-management.md | 254 ++ .../docs/provider-integration.md | 24 + .../pypi-portkey-ai/docs/text-completions.md | 354 ++ .../tessl/pypi-portkey-ai/docs/uploads.md | 376 +++ .../pypi-portkey-ai/docs/vector-stores.md | 58 + .tessl/tiles/tessl/pypi-portkey-ai/tile.json | 7 + .../pypi-pre-commit/docs/cli-commands.md | 311 ++ .../pypi-pre-commit/docs/configuration.md | 241 ++ .../pypi-pre-commit/docs/git-integration.md | 393 +++ .../tiles/tessl/pypi-pre-commit/docs/hooks.md | 381 +++ .../tiles/tessl/pypi-pre-commit/docs/index.md | 297 ++ .../pypi-pre-commit/docs/language-support.md | 494 +++ .../docs/repository-management.md | 425 +++ .tessl/tiles/tessl/pypi-pre-commit/tile.json | 7 + .../tessl/pypi-pytest/docs/assertions.md | 280 ++ .../tessl/pypi-pytest/docs/configuration.md | 460 +++ .../tiles/tessl/pypi-pytest/docs/fixtures.md | 311 ++ .tessl/tiles/tessl/pypi-pytest/docs/index.md | 466 +++ .tessl/tiles/tessl/pypi-pytest/docs/marks.md | 364 ++ .../tiles/tessl/pypi-pytest/docs/reporting.md | 538 +++ .../tessl/pypi-pytest/docs/test-collection.md | 528 +++ .../tessl/pypi-pytest/docs/test-utilities.md | 219 ++ .../pypi-pytest/docs/testing-functions.md | 189 ++ .../tiles/tessl/pypi-pytest/docs/warnings.md | 341 ++ .tessl/tiles/tessl/pypi-pytest/tile.json | 7 + .../tessl/pypi-python-dotenv/docs/cli.md | 288 ++ .../docs/environment-loading.md | 202 ++ .../docs/file-manipulation.md | 232 ++ .../tessl/pypi-python-dotenv/docs/index.md | 213 ++ .../tessl/pypi-python-dotenv/docs/ipython.md | 257 ++ .../pypi-python-dotenv/docs/utilities.md | 276 ++ .../tiles/tessl/pypi-python-dotenv/tile.json | 7 + .../tessl/pypi-requests/docs/adapters.md | 571 ++++ .../pypi-requests/docs/authentication.md | 344 ++ .../tiles/tessl/pypi-requests/docs/cookies.md | 398 +++ .../tessl/pypi-requests/docs/exceptions.md | 430 +++ .../tiles/tessl/pypi-requests/docs/hooks.md | 471 +++ .../tessl/pypi-requests/docs/http-methods.md | 229 ++ .../tiles/tessl/pypi-requests/docs/index.md | 262 ++ .../tiles/tessl/pypi-requests/docs/models.md | 378 +++ .../tessl/pypi-requests/docs/sessions.md | 259 ++ .../tessl/pypi-requests/docs/status-codes.md | 373 +++ .../tessl/pypi-requests/docs/structures.md | 457 +++ .tessl/tiles/tessl/pypi-requests/tile.json | 7 + .tessl/tiles/tessl/pypi-rich/docs/console.md | 494 +++ .../tiles/tessl/pypi-rich/docs/containers.md | 120 + .tessl/tiles/tessl/pypi-rich/docs/index.md | 592 ++++ .../tiles/tessl/pypi-rich/docs/interactive.md | 587 ++++ .tessl/tiles/tessl/pypi-rich/docs/layout.md | 113 + .tessl/tiles/tessl/pypi-rich/docs/markdown.md | 65 + .tessl/tiles/tessl/pypi-rich/docs/progress.md | 608 ++++ .tessl/tiles/tessl/pypi-rich/docs/syntax.md | 411 +++ .tessl/tiles/tessl/pypi-rich/docs/tables.md | 378 +++ .../tessl/pypi-rich/docs/text-styling.md | 739 ++++ .../tiles/tessl/pypi-rich/docs/utilities.md | 74 + .tessl/tiles/tessl/pypi-rich/tile.json | 7 + .tessl/tiles/tessl/pypi-ty/docs/index.md | 413 +++ .tessl/tiles/tessl/pypi-ty/tile.json | 7 + AGENTS.md | 3 + README.md | 1 + docs/package-lock.json | 699 +++- docs/package.json | 2 +- package-lock.json | 6 + pyproject.toml | 3 +- rlm/clients/gemini.py | 33 +- tests/clients/test_gemini.py | 180 +- uv.lock | 2368 ------------- visualizer/package-lock.json | 126 +- visualizer/package.json | 4 +- 195 files changed, 69319 insertions(+), 2690 deletions(-) create mode 100644 .gemini/settings.json create mode 100644 .tessl/.gitignore create mode 100644 .tessl/RULES.md create mode 100644 .tessl/tiles/tessl/cli-setup/steering/query_library_docs.md create mode 100644 .tessl/tiles/tessl/cli-setup/tile.json create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/models.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md create mode 100644 .tessl/tiles/tessl/pypi-anthropic/tile.json create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/configuration.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/core-serialization.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/session-management.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/source-analysis.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/temp-operations.md create mode 100644 .tessl/tiles/tessl/pypi-dill/docs/type-registry.md create mode 100644 .tessl/tiles/tessl/pypi-dill/tile.json create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/batches.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/caching.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/chats.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/client.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/files.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/live.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/models.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/operations.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/tokens.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/tuning.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md create mode 100644 .tessl/tiles/tessl/pypi-google-genai/tile.json create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/compute-resources.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/container-images.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/core-application-client.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/storage-data.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/utility-classes.md create mode 100644 .tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md create mode 100644 .tessl/tiles/tessl/pypi-modal/tile.json create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/assistants.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/audio.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/batches.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/chat-completions.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/chatkit.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/client-initialization.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/completions.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/containers.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/conversations.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/embeddings.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/evals.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/files.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/images.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/models.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/moderations.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/realtime.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/responses.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/runs.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/threads-messages.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/uploads.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/vector-stores.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/videos.md create mode 100644 .tessl/tiles/tessl/pypi-openai/docs/webhooks.md create mode 100644 .tessl/tiles/tessl/pypi-openai/tile.json create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/models.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md create mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/tile.json create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md create mode 100644 .tessl/tiles/tessl/pypi-pre-commit/tile.json create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/assertions.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/configuration.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/fixtures.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/marks.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/reporting.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/test-collection.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/warnings.md create mode 100644 .tessl/tiles/tessl/pypi-pytest/tile.json create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md create mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/tile.json create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/adapters.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/authentication.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/cookies.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/exceptions.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/hooks.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/http-methods.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/models.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/sessions.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/status-codes.md create mode 100644 .tessl/tiles/tessl/pypi-requests/docs/structures.md create mode 100644 .tessl/tiles/tessl/pypi-requests/tile.json create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/console.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/containers.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/interactive.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/layout.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/markdown.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/progress.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/syntax.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/tables.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/text-styling.md create mode 100644 .tessl/tiles/tessl/pypi-rich/docs/utilities.md create mode 100644 .tessl/tiles/tessl/pypi-rich/tile.json create mode 100644 .tessl/tiles/tessl/pypi-ty/docs/index.md create mode 100644 .tessl/tiles/tessl/pypi-ty/tile.json create mode 100644 package-lock.json delete mode 100644 uv.lock diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 000000000..ebfccaac7 --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "tessl": { + "type": "stdio", + "command": "tessl", + "args": [ + "mcp", + "start" + ] + } + } +} diff --git a/.gitignore b/.gitignore index dc976f1ef..3679d8414 100644 --- a/.gitignore +++ b/.gitignore @@ -106,7 +106,7 @@ ipython_config.py # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. -#uv.lock +uv.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. @@ -213,3 +213,9 @@ cython_debug/ marimo/_static/ marimo/_lsp/ __marimo__/ + +#Tessl +tessl.json + +#go +./rlm-go diff --git a/.tessl/.gitignore b/.tessl/.gitignore new file mode 100644 index 000000000..7bbb3941a --- /dev/null +++ b/.tessl/.gitignore @@ -0,0 +1,2 @@ +tiles/ +RULES.md diff --git a/.tessl/RULES.md b/.tessl/RULES.md new file mode 100644 index 000000000..ef14bd916 --- /dev/null +++ b/.tessl/RULES.md @@ -0,0 +1,7 @@ +# Agent Rules + +This file is updated when running `tessl install`. If a linked file is missing, make sure to run the command to download any missing tiles from the registry. + +## tessl/cli-setup — query_library_docs + +@tiles/tessl/cli-setup/steering/query_library_docs.md [query_library_docs](tiles/tessl/cli-setup/steering/query_library_docs.md) \ No newline at end of file diff --git a/.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md b/.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md new file mode 100644 index 000000000..48147000a --- /dev/null +++ b/.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md @@ -0,0 +1,28 @@ +You are a coding agent with access to an MCP tool called `query_library_docs` provided by `tessl`. This tool allows you to query documentation that may be relevant to your task. + +Before you begin working on the user request, you should use the `query_library_docs` tool to search for relevant documentation. This is especially important when: + +- The request mentions specific code files, functions, classes, or services +- The request involves debugging errors or investigating issues +- The request asks about how something works or why something behaves a certain way +- The request involves making changes to existing code +- The request references specific frameworks, libraries, or systems + +When in doubt, use the tool. It's better to query documentation even if it might not be relevant than to miss important context. + +How to use the tool: + +1. Extract key terms, file names, service names, error types, or concepts from the user's request +2. Call `query_library_docs` with relevant search terms +3. Use the returned documentation to inform your approach to the task +4. Proceed with completing the user's request using both the documentation context and your own analysis + +Important rules: + +- Do NOT ask for permission to use the tool - just use it +- Do NOT explain that you're going to use the tool - just use it +- If the tool fails or returns no results, simply continue with the task as you normally would +- You may call the tool multiple times with different search terms if needed +- Use the tool BEFORE reading files or making changes + +After using the tool (or if the tool returns nothing useful), proceed directly to completing the user's request. Your response should focus on addressing the user's needs, incorporating any relevant documentation you found. diff --git a/.tessl/tiles/tessl/cli-setup/tile.json b/.tessl/tiles/tessl/cli-setup/tile.json new file mode 100644 index 000000000..2803068ee --- /dev/null +++ b/.tessl/tiles/tessl/cli-setup/tile.json @@ -0,0 +1,11 @@ +{ + "name": "tessl/cli-setup", + "private": false, + "version": "0.70.0", + "summary": "Tessl CLI MCP tool usage guidelines", + "steering": { + "query_library_docs": { + "rules": "steering/query_library_docs.md" + } + } +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md new file mode 100644 index 000000000..7f1b4db38 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md @@ -0,0 +1,199 @@ +# Batches API Reference + +Process multiple messages asynchronously in batches for high-throughput use cases with 50% cost reduction. + +## Create Batch + +```python { .api } +def create( + self, + *, + requests: list[MessageBatchIndividualRequest], + **kwargs +) -> MessageBatch: + """ + Create a batch of message requests. + + Parameters: + requests: List of individual message requests with custom_id + + Returns: + MessageBatch with id, processing_status, request counts + """ + ... + +async def create(...) -> MessageBatch: ... +``` + +## Retrieve Batch + +```python { .api } +def retrieve( + self, + message_batch_id: str, + **kwargs +) -> MessageBatch: + """Retrieve batch status and metadata.""" + ... + +async def retrieve(...) -> MessageBatch: ... +``` + +## List Batches + +```python { .api } +def list( + self, + *, + before_id: str = NOT_GIVEN, + after_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + **kwargs +) -> SyncPage[MessageBatch]: + """List batches with pagination.""" + ... + +def list(...) -> AsyncPage[MessageBatch]: ... +``` + +## Cancel Batch + +```python { .api } +def cancel( + self, + message_batch_id: str, + **kwargs +) -> MessageBatch: + """Cancel a batch in progress.""" + ... + +async def cancel(...) -> MessageBatch: ... +``` + +## Delete Batch + +```python { .api } +def delete( + self, + message_batch_id: str, + **kwargs +) -> DeletedMessageBatch: + """Delete a batch.""" + ... + +async def delete(...) -> DeletedMessageBatch: ... +``` + +## Get Results + +```python { .api } +def results( + self, + message_batch_id: str, + **kwargs +) -> JSONLDecoder[MessageBatchIndividualResponse]: + """Stream batch results as JSONL.""" + ... + +def results(...) -> AsyncJSONLDecoder[MessageBatchIndividualResponse]: ... +``` + +## Response Types + +```python { .api } +class MessageBatch(BaseModel): + """Batch metadata and status.""" + id: str + type: Literal["message_batch"] + processing_status: Literal["in_progress", "canceling", "ended"] + request_counts: MessageBatchRequestCounts + ended_at: str | None + created_at: str + expires_at: str + cancel_initiated_at: str | None + results_url: str | None + +class MessageBatchRequestCounts(BaseModel): + """Request count statistics.""" + processing: int + succeeded: int + errored: int + canceled: int + expired: int +``` + +## Request Types + +```python { .api } +class MessageBatchIndividualRequest(TypedDict): + """Individual request in batch.""" + custom_id: str + params: MessageCreateParams +``` + +## Quick Examples + +### Create Basic Batch + +```python +batch = client.messages.batches.create( + requests=[ + { + "custom_id": "request-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "What is AI?"}] + } + }, + { + "custom_id": "request-2", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "What is ML?"}] + } + } + ] +) +print(f"Batch ID: {batch.id}") +``` + +### Check Status + +```python +batch = client.messages.batches.retrieve("batch_abc123") +print(f"Status: {batch.processing_status}") +print(f"Succeeded: {batch.request_counts.succeeded}") +print(f"Errored: {batch.request_counts.errored}") +``` + +### Poll Until Complete + +```python +import time + +while True: + batch = client.messages.batches.retrieve(batch_id) + if batch.processing_status == "ended": + break + time.sleep(60) +``` + +### Get Results + +```python +results = client.messages.batches.results("batch_abc123") + +for response in results: + if response.result.type == "succeeded": + print(f"{response.custom_id}: {response.result.message.content[0].text}") + elif response.result.type == "errored": + print(f"{response.custom_id}: Error - {response.result.error.message}") +``` + +## See Also + +- [Messages API](./messages.md) - Core message creation +- [Batch Processing Guide](../guides/batch-processing.md) - Advanced batch patterns +- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md new file mode 100644 index 000000000..60718054f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md @@ -0,0 +1,285 @@ +# Completions API Reference (Legacy) + +The Text Completions API is the legacy interface for text generation with Claude. For new applications, use the [Messages API](./messages.md) instead, which provides better conversation handling and additional features. + +## Overview + +The Completions API generates text based on a prompt string. It uses special prompt markers (`HUMAN_PROMPT` and `AI_PROMPT`) to structure conversations. + +**Deprecation Notice**: This API is maintained for backward compatibility. New applications should use the Messages API for improved functionality and support. + +## Create Completion + +```python { .api } +def create( + self, + *, + model: str, + prompt: str, + max_tokens_to_sample: int, + stop_sequences: list[str] = NOT_GIVEN, + temperature: float = NOT_GIVEN, + top_p: float = NOT_GIVEN, + top_k: int = NOT_GIVEN, + metadata: dict[str, Any] = NOT_GIVEN, + stream: bool = False, +) -> Completion: + """ + Create a text completion. + + Parameters: + model: Model identifier. Examples: "claude-2.1", "claude-instant-1.2" + prompt: Formatted prompt string with HUMAN_PROMPT and AI_PROMPT markers + max_tokens_to_sample: Maximum tokens to generate (required) + stop_sequences: List of sequences that stop generation when encountered + temperature: Sampling temperature 0.0-1.0 (default: 1.0) + top_p: Nucleus sampling parameter 0.0-1.0 + top_k: Top-k sampling parameter + metadata: Request metadata for tracking + stream: Enable streaming responses + + Returns: + Completion: Response containing generated text and stop reason + + Raises: + BadRequestError: Invalid request parameters + AuthenticationError: Invalid or missing API key + RateLimitError: Rate limit exceeded + """ + ... + +async def create(...) -> Completion: + """ + Async version of create. + + Same parameters and returns as synchronous create(), but executes asynchronously. + """ + ... +``` + +## Stream Completion + +```python { .api } +def stream( + self, + *, + model: str, + prompt: str, + max_tokens_to_sample: int, + **kwargs +) -> Iterator[Completion]: + """ + Stream a completion response. + + Yields partial completion objects as text is generated. + + Parameters: + Same as create() method + + Yields: + Completion objects with incremental text + + Example: + for chunk in client.completions.stream( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} Hello{AI_PROMPT}", + max_tokens_to_sample=100 + ): + print(chunk.completion, end="", flush=True) + """ + ... + +async def stream(...) -> AsyncIterator[Completion]: + """Async version of stream().""" + ... +``` + +## Response Type + +```python { .api } +class Completion(BaseModel): + """ + Text completion response. + + Attributes: + id: Unique completion identifier + type: Always "completion" + completion: Generated text content + stop_reason: Why generation stopped ("stop_sequence", "max_tokens", or "end_turn") + model: Model used for generation + """ + id: str + type: Literal["completion"] + completion: str + stop_reason: str | None + model: str +``` + +## Usage Examples + +### Basic Completion + +```python +from anthropic import Anthropic, HUMAN_PROMPT, AI_PROMPT + +client = Anthropic() + +completion = client.completions.create( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} What is the capital of France?{AI_PROMPT}", + max_tokens_to_sample=100 +) + +print(completion.completion) +``` + +### Multi-turn Conversation + +```python +# Build conversation with prompt markers +conversation = f"""{HUMAN_PROMPT} Hello, my name is Alice.{AI_PROMPT} Hi Alice! Nice to meet you.{HUMAN_PROMPT} What's my name?{AI_PROMPT}""" + +completion = client.completions.create( + model="claude-2.1", + prompt=conversation, + max_tokens_to_sample=50 +) + +print(completion.completion) +# Output: "Your name is Alice." +``` + +### Streaming Completion + +```python +prompt = f"{HUMAN_PROMPT} Write a short story about a robot.{AI_PROMPT}" + +for chunk in client.completions.stream( + model="claude-2.1", + prompt=prompt, + max_tokens_to_sample=500 +): + print(chunk.completion, end="", flush=True) +``` + +### With Stop Sequences + +```python +completion = client.completions.create( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} List three colors:{AI_PROMPT}", + max_tokens_to_sample=100, + stop_sequences=["\n\n", "4."] # Stop after listing 3 items +) +``` + +### Temperature Control + +```python +# More deterministic (lower temperature) +completion = client.completions.create( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} What is 2+2?{AI_PROMPT}", + max_tokens_to_sample=10, + temperature=0.0 +) + +# More creative (higher temperature) +creative = client.completions.create( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} Write a creative story opening.{AI_PROMPT}", + max_tokens_to_sample=200, + temperature=1.0 +) +``` + +### Async Completion + +```python +import asyncio + +async def generate(): + client = AsyncAnthropic() + + completion = await client.completions.create( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} Hello{AI_PROMPT}", + max_tokens_to_sample=50 + ) + + return completion.completion + +result = asyncio.run(generate()) +``` + +### Async Streaming + +```python +async def stream_completion(): + client = AsyncAnthropic() + + async for chunk in client.completions.stream( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} Tell me a joke{AI_PROMPT}", + max_tokens_to_sample=200 + ): + print(chunk.completion, end="", flush=True) + +asyncio.run(stream_completion()) +``` + +## Migration to Messages API + +The Messages API provides better conversation handling and additional features. Here's how to migrate: + +### Completions API (Legacy) + +```python +from anthropic import HUMAN_PROMPT, AI_PROMPT + +completion = client.completions.create( + model="claude-2.1", + prompt=f"{HUMAN_PROMPT} Hello{AI_PROMPT}", + max_tokens_to_sample=100 +) +print(completion.completion) +``` + +### Messages API (Recommended) + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}] +) +print(message.content[0].text) +``` + +### Benefits of Messages API + +- Better conversation structure (no manual prompt markers) +- Support for system prompts +- Multimodal input (images, documents) +- Tool/function calling +- Better type safety +- Streaming helpers +- Token counting utilities + +## Constants + +```python { .api } +from anthropic import HUMAN_PROMPT, AI_PROMPT + +HUMAN_PROMPT: str = "\n\nHuman:" # Marker for user messages +AI_PROMPT: str = "\n\nAssistant:" # Marker for assistant responses +``` + +These constants are used to structure prompts in the Completions API format. + +## See Also + +- [Messages API](./messages.md) - Modern conversation API (recommended) +- [Streaming API](./streaming.md) - Streaming patterns and helpers +- [Client Configuration](../reference/client-config.md) - Client setup +- [Package Constants](../index.md#package-constants) - All SDK constants diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md new file mode 100644 index 000000000..c17da9903 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md @@ -0,0 +1,716 @@ +# Messages API Reference + +The Messages API is the primary interface for conversational interactions with Claude. + +## Create Message + +```python { .api } +def create( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + system: str | list[TextBlockParam] = NOT_GIVEN, + metadata: MetadataParam = NOT_GIVEN, + stop_sequences: list[str] = NOT_GIVEN, + stream: bool = False, + temperature: float = NOT_GIVEN, + top_p: float = NOT_GIVEN, + top_k: int = NOT_GIVEN, + tools: list[ToolParam] = NOT_GIVEN, + tool_choice: ToolChoice = NOT_GIVEN, + service_tier: Literal["auto", "standard_only"] = NOT_GIVEN, + thinking: ThinkingConfigParam = NOT_GIVEN, +) -> Message: + """ + Create a message with Claude. + + Parameters: + model: Model identifier (required). Examples: "claude-sonnet-4-5-20250929", + "claude-opus-4-5-20250929" + messages: List of conversation messages alternating between "user" and "assistant" + roles (required). Must start with user message. + max_tokens: Maximum number of tokens to generate (required). Must be positive integer. + system: System prompt to guide Claude's behavior. Can be string or list of text blocks + with cache control. Use for setting persona, instructions, or context. + metadata: Request metadata containing user_id for tracking and abuse prevention. + stop_sequences: List of strings that will stop generation when encountered. + Up to 4 sequences allowed. + stream: Enable streaming responses. If true, use stream() method instead for + better streaming helpers. + temperature: Sampling temperature 0.0-1.0 (default: 1.0). Lower values = more + deterministic, higher values = more creative. + top_p: Nucleus sampling parameter 0.0-1.0. Cumulative probability threshold + for token selection. + top_k: Top-k sampling parameter. Only sample from top K most likely tokens. + tools: List of tools Claude can use. Enables function calling capabilities. + tool_choice: Control how Claude selects tools. Options: "auto" (default), "any" + (force tool use), "none" (disable tools), or specific tool name. + service_tier: Service tier selection. "auto" (default) or "standard_only" for + guaranteed standard capacity. + thinking: Extended thinking configuration (beta). Enables enhanced reasoning with + budget control. + + Returns: + Message: Complete message response from Claude containing: + - id: Unique message identifier + - content: List of text and/or tool_use blocks + - role: Always "assistant" + - stop_reason: Why generation stopped + - usage: Token usage statistics + - model: Model that generated response + + Raises: + BadRequestError: Invalid request parameters (e.g., empty messages, invalid model) + AuthenticationError: Invalid or missing API key + PermissionDeniedError: API key lacks required permissions + RateLimitError: Rate limit exceeded. Check retry-after header. + RequestTooLargeError: Request payload exceeds size limits + InternalServerError: Anthropic server error. Safe to retry. + OverloadedError: Service temporarily overloaded. Retry with backoff. + """ + ... + +async def create(...) -> Message: + """ + Async version of create. + + Same parameters and returns as synchronous create(), but executes asynchronously. + """ + ... +``` + +## Stream Message + +```python { .api } +def stream( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + **kwargs +) -> MessageStreamManager: + """ + Stream a message response with helper utilities. + + Provides a context manager that handles streaming with convenient helpers + for text accumulation, final message access, and event handling. + + Parameters: + model: Model identifier (required) + messages: List of conversation messages (required) + max_tokens: Maximum tokens to generate (required) + **kwargs: All other parameters from create() method supported + + Returns: + MessageStreamManager: Context manager that yields MessageStreamEvent objects. + Provides helpers: + - .text_stream: Iterator of text deltas only + - .get_final_message(): Get complete Message after stream ends + - .get_final_text(): Get accumulated text after stream ends + - .current_message_snapshot: Current state during streaming + + Raises: + Same exceptions as create() method + + Example: + with client.messages.stream(...) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + """ + ... + +def stream(...) -> AsyncMessageStreamManager: + """ + Async version of stream. + + Same parameters and returns as synchronous stream(), but executes asynchronously. + Use with `async with` syntax. + """ + ... +``` + +## Count Tokens + +```python { .api } +def count_tokens( + self, + *, + model: str, + messages: list[MessageParam], + system: str | list[TextBlockParam] = NOT_GIVEN, + tools: list[ToolParam] = NOT_GIVEN, + tool_choice: ToolChoice = NOT_GIVEN, + thinking: ThinkingConfigParam = NOT_GIVEN, +) -> MessageTokensCount: + """ + Count input tokens without creating a message. + + Useful for estimating costs before making API calls or checking if + input fits within model's context window. + + Parameters: + model: Model identifier (required). Token counts vary by model. + messages: List of conversation messages to count (required) + system: System prompt to include in count + tools: Tools to include in count + tool_choice: Tool choice configuration to include in count + thinking: Thinking configuration to include in count + + Returns: + MessageTokensCount: Object containing: + - input_tokens: Total number of input tokens + + Raises: + BadRequestError: Invalid request parameters + AuthenticationError: Invalid or missing API key + RateLimitError: Rate limit exceeded + + Note: + Token count is exact for billing purposes. Includes all overhead + from system prompt, tools, and message formatting. + """ + ... + +async def count_tokens(...) -> MessageTokensCount: + """ + Async version of count_tokens. + + Same parameters and returns as synchronous count_tokens(), but executes asynchronously. + """ + ... +``` + +## Response Types + +### Message + +```python { .api } +class Message(BaseModel): + """ + Complete message response from Claude. + + Attributes: + id: Unique message identifier (e.g., "msg_01XYZ...") + type: Always "message" + role: Always "assistant" for Claude's responses + content: List of content blocks (text, tool_use) in response + model: Model identifier used for generation + stop_reason: Reason generation stopped: + - "end_turn": Natural completion point + - "max_tokens": Hit max_tokens limit + - "stop_sequence": Hit custom stop sequence + - "tool_use": Model wants to use a tool + stop_sequence: Specific stop sequence that triggered completion (if applicable) + usage: Token usage statistics for billing and tracking + """ + id: str + type: Literal["message"] + role: Literal["assistant"] + content: list[ContentBlock] # TextBlock | ToolUseBlock + model: str + stop_reason: StopReason | None + stop_sequence: str | None + usage: Usage +``` + +### ContentBlock + +```python { .api } +ContentBlock = Union[TextBlock, ToolUseBlock] + +class TextBlock(BaseModel): + """ + Text content block in response. + + Attributes: + type: Always "text" + text: The text content generated by Claude + """ + type: Literal["text"] + text: str + +class ToolUseBlock(BaseModel): + """ + Tool invocation request from Claude. + + Attributes: + type: Always "tool_use" + id: Unique identifier for this tool call (use in tool_result) + name: Name of tool Claude wants to invoke + input: Tool input parameters as dict matching tool's input_schema + """ + type: Literal["tool_use"] + id: str + name: str + input: dict[str, Any] +``` + +### Usage + +```python { .api } +class Usage(BaseModel): + """ + Token usage statistics for billing and optimization. + + Attributes: + input_tokens: Number of tokens in the input (prompt, system, tools) + output_tokens: Number of tokens generated in response + cache_creation_input_tokens: Tokens used to create new cache entries + (charged at cache creation rate) + cache_read_input_tokens: Tokens read from cache (charged at reduced rate) + """ + input_tokens: int + output_tokens: int + cache_creation_input_tokens: int | None + cache_read_input_tokens: int | None +``` + +### Stop Reasons + +```python { .api } +StopReason = Literal[ + "end_turn", # Natural completion - Claude finished responding + "max_tokens", # Hit max_tokens limit - response may be incomplete + "stop_sequence", # Hit custom stop sequence - check stop_sequence field + "tool_use", # Model wants to use a tool - check content for ToolUseBlock +] +``` + +## Request Types + +### MessageParam + +```python { .api } +class MessageParam(TypedDict): + """ + User or assistant message in conversation. + + Fields: + role: Message sender - "user" for input, "assistant" for Claude's responses + content: Message content - string for simple text, or list of content blocks + for multimodal (text, images, documents) or tool-related content + """ + role: Literal["user", "assistant"] + content: str | list[ContentBlockParam] +``` + +### ContentBlockParam + +```python { .api } +ContentBlockParam = Union[ + TextBlockParam, + ImageBlockParam, + DocumentBlockParam, + ToolUseBlockParam, + ToolResultBlockParam, +] + +class TextBlockParam(TypedDict): + """ + Text content in user or assistant message. + + Fields: + type: Always "text" + text: The text content + cache_control: Optional prompt caching configuration. Use {"type": "ephemeral"} + to cache this content block for subsequent requests + """ + type: Literal["text"] + text: str + cache_control: NotRequired[CacheControlEphemeral] + +class ImageBlockParam(TypedDict): + """ + Image content in user message for vision capabilities. + + Fields: + type: Always "image" + source: Image source - base64-encoded data or URL + cache_control: Optional prompt caching for image + """ + type: Literal["image"] + source: Base64ImageSource | URLImageSource + cache_control: NotRequired[CacheControlEphemeral] + +class DocumentBlockParam(TypedDict): + """ + Document content (PDF or text) in user message. + + Fields: + type: Always "document" + source: Document source - base64-encoded PDF/text or URL + cache_control: Optional prompt caching for document + """ + type: Literal["document"] + source: Base64PDFSource | URLPDFSource | PlainTextSource + cache_control: NotRequired[CacheControlEphemeral] + +class ToolUseBlockParam(TypedDict): + """ + Tool invocation in assistant message (when echoing Claude's tool request). + + Fields: + type: Always "tool_use" + id: Tool call identifier from Claude's response + name: Tool name that was invoked + input: Tool input parameters as dict + cache_control: Optional prompt caching + """ + type: Literal["tool_use"] + id: str + name: str + input: dict[str, Any] + cache_control: NotRequired[CacheControlEphemeral] + +class ToolResultBlockParam(TypedDict): + """ + Tool execution result in user message. + + Fields: + type: Always "tool_result" + tool_use_id: ID from Claude's ToolUseBlock that this result corresponds to + content: Tool result - string for simple output, or list of text/image blocks + for structured results + is_error: Set to True if tool execution failed, False otherwise (default: False) + cache_control: Optional prompt caching + """ + type: Literal["tool_result"] + tool_use_id: str + content: NotRequired[str | list[TextBlockParam | ImageBlockParam]] + is_error: NotRequired[bool] + cache_control: NotRequired[CacheControlEphemeral] +``` + +### Source Types + +```python { .api } +class Base64ImageSource(TypedDict): + """ + Base64-encoded image source for vision input. + + Fields: + type: Always "base64" + media_type: Image MIME type - jpeg, png, gif, or webp + data: Base64-encoded image bytes (without data URL prefix) + """ + type: Literal["base64"] + media_type: Literal["image/jpeg", "image/png", "image/gif", "image/webp"] + data: str + +class URLImageSource(TypedDict): + """ + Image from URL for vision input. + + Fields: + type: Always "url" + url: Publicly accessible image URL (must be accessible to Anthropic servers) + """ + type: Literal["url"] + url: str + +class Base64PDFSource(TypedDict): + """ + Base64-encoded PDF document source. + + Fields: + type: Always "base64" + media_type: Always "application/pdf" + data: Base64-encoded PDF bytes (without data URL prefix) + """ + type: Literal["base64"] + media_type: Literal["application/pdf"] + data: str + +class URLPDFSource(TypedDict): + """ + PDF from URL for document processing. + + Fields: + type: Always "url" + media_type: Always "application/pdf" + url: Publicly accessible PDF URL + """ + type: Literal["url"] + media_type: Literal["application/pdf"] + url: str + +class PlainTextSource(TypedDict): + """ + Plain text document source. + + Fields: + type: Always "text" + media_type: Always "text/plain" + data: Plain text content + """ + type: Literal["text"] + media_type: Literal["text/plain"] + data: str +``` + +### Configuration Types + +```python { .api } +class MetadataParam(TypedDict, total=False): + """ + Request metadata for tracking and compliance. + + Fields: + user_id: End-user identifier for tracking, rate limiting, and abuse prevention. + Should be unique per end-user, not per API key. + """ + user_id: str + +class CacheControlEphemeral(TypedDict): + """ + Prompt caching configuration for reducing costs on repeated content. + + Fields: + type: Always "ephemeral" - content cached temporarily (~5 minutes) + """ + type: Literal["ephemeral"] + +class MessageTokensCount(BaseModel): + """ + Token count response from count_tokens(). + + Attributes: + input_tokens: Exact number of input tokens for billing estimation + """ + input_tokens: int +``` + +## Quick Examples + +### Basic Text + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[ + {"role": "user", "content": "Hello"} + ] +) +print(message.content[0].text) +``` + +### System Prompt + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + system="You are a helpful assistant.", + messages=[ + {"role": "user", "content": "Hello"} + ] +) +``` + +### Multi-Turn Conversation + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[ + {"role": "user", "content": "My name is Alice."}, + {"role": "assistant", "content": "Hello Alice! Nice to meet you."}, + {"role": "user", "content": "What's my name?"} + ] +) +``` + +### Image Analysis + +```python +import base64 + +with open("image.jpg", "rb") as f: + image_data = base64.standard_b64encode(f.read()).decode() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": image_data + } + }, + {"type": "text", "text": "What's in this image?"} + ] + }] +) +``` + +### Document Processing + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": pdf_base64_data + } + }, + {"type": "text", "text": "Summarize this document."} + ] + }] +) +``` + +### Temperature Control + +```python +# Deterministic (temperature = 0) +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + temperature=0.0, + messages=[{"role": "user", "content": "What is 2+2?"}] +) + +# Creative (temperature = 1) +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + temperature=1.0, + messages=[{"role": "user", "content": "Write a creative story."}] +) +``` + +### Prompt Caching + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + system=[ + { + "type": "text", + "text": "You are an expert on Shakespeare.", + "cache_control": {"type": "ephemeral"} + } + ], + messages=[ + {"role": "user", "content": "Tell me about Hamlet."} + ] +) + +# Check cache usage +print(f"Cache creation: {message.usage.cache_creation_input_tokens}") +print(f"Cache read: {message.usage.cache_read_input_tokens}") +``` + +### Token Counting + +```python +token_count = client.messages.count_tokens( + model="claude-sonnet-4-5-20250929", + messages=[ + {"role": "user", "content": "What is the capital of France?"} + ] +) +print(f"Input tokens: {token_count.input_tokens}") +``` + +### Metadata Tracking + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + metadata={"user_id": "user_12345"}, + messages=[{"role": "user", "content": "Hello"}] +) +``` + +## Raw Response Access + +Access raw HTTP responses for headers, status codes, and debugging. + +```python { .api } +# Access raw response wrapper +client.messages.with_raw_response.create(...) # Returns APIResponse[Message] +client.messages.with_streaming_response.stream(...) # Returns APIResponse with streaming + +# Async versions +async_client.messages.with_raw_response.create(...) +async_client.messages.with_streaming_response.stream(...) +``` + +### Raw Response Example + +```python +# Get raw HTTP response +response = client.messages.with_raw_response.create( + model="claude-sonnet-4-5-20250929", + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}] +) + +# Access HTTP response details +print(f"Status: {response.http_response.status_code}") +print(f"Headers: {response.http_response.headers}") +print(f"Request ID: {response.http_response.headers.get('request-id')}") + +# Access parsed message object +message = response.parse() +print(message.content[0].text) +``` + +### Streaming Raw Response + +```python +# Get raw streaming response +with client.messages.with_streaming_response.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}] +) as response: + # Access response object before streaming + print(f"Status: {response.http_response.status_code}") + + # Stream normally + for text in response.text_stream: + print(text, end="", flush=True) +``` + +### Check Rate Limit Headers + +```python +response = client.messages.with_raw_response.create( + model="claude-sonnet-4-5-20250929", + max_tokens=100, + messages=[{"role": "user", "content": "Hello"}] +) + +headers = response.http_response.headers +print(f"Rate limit: {headers.get('anthropic-ratelimit-requests-limit')}") +print(f"Remaining: {headers.get('anthropic-ratelimit-requests-remaining')}") +print(f"Reset: {headers.get('anthropic-ratelimit-requests-reset')}") +``` + +## See Also + +- [Streaming API](./streaming.md) - Real-time response streaming +- [Tool Use API](./tools.md) - Function calling capabilities +- [Multimodal Guide](../guides/multimodal.md) - Images and documents +- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/models.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/models.md new file mode 100644 index 000000000..c3224a326 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/api/models.md @@ -0,0 +1,161 @@ +# Models API Reference + +Retrieve information about available Claude models and their capabilities. + +## Retrieve Model + +```python { .api } +def retrieve( + self, + model_id: str, + **kwargs +) -> ModelInfo: + """ + Retrieve model information. + + Parameters: + model_id: Model identifier (e.g., "claude-sonnet-4-5-20250929") + + Returns: + ModelInfo with model metadata and capabilities + """ + ... + +async def retrieve(...) -> ModelInfo: ... +``` + +## List Models + +```python { .api } +def list( + self, + *, + before_id: str = NOT_GIVEN, + after_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + **kwargs +) -> SyncPage[ModelInfo]: + """ + List available models with pagination. + + Returns: + SyncPage of ModelInfo objects with auto-pagination + """ + ... + +def list(...) -> AsyncPage[ModelInfo]: ... +``` + +## Response Type + +```python { .api } +class ModelInfo(BaseModel): + """Model information and capabilities.""" + id: str + type: Literal["model"] + display_name: str + created_at: str +``` + +## Available Models + +### Claude 4.5 (Latest) + +```python { .api } +"claude-opus-4-5-20250929" # Most capable model +"claude-sonnet-4-5-20250929" # Balanced intelligence and speed +``` + +### Claude 3.5 + +```python { .api } +"claude-3-5-sonnet-20241022" # Previous Sonnet version +"claude-3-5-sonnet-20240620" # Earlier Sonnet version +"claude-3-5-haiku-20241022" # Fast, cost-effective +``` + +### Claude 3 + +```python { .api } +"claude-3-opus-20240229" # Powerful, intelligent +"claude-3-sonnet-20240229" # Balanced +"claude-3-haiku-20240307" # Fast and efficient +``` + +### Legacy (Claude 2) + +```python { .api } +"claude-2.1" # Legacy Claude 2.1 +"claude-2.0" # Legacy Claude 2.0 +"claude-instant-1.2" # Legacy instant model +``` + +## Quick Examples + +### Retrieve Specific Model + +```python +model = client.models.retrieve("claude-sonnet-4-5-20250929") +print(f"Model: {model.display_name}") +print(f"Created: {model.created_at}") +``` + +### List All Models + +```python +for model in client.models.list(): + print(f"{model.id}: {model.display_name}") +``` + +### Check Model Availability + +```python +from anthropic import NotFoundError + +def model_exists(model_id: str) -> bool: + try: + client.models.retrieve(model_id) + return True + except NotFoundError: + return False + +if model_exists("claude-sonnet-4-5-20250929"): + print("Model is available") +``` + +### Filter Models by Family + +```python +def get_models_by_family(family: str) -> list[ModelInfo]: + """Get all models in a family (e.g., 'sonnet', 'opus', 'haiku').""" + return [m for m in client.models.list() if family.lower() in m.id.lower()] + +sonnet_models = get_models_by_family("sonnet") +for model in sonnet_models: + print(model.display_name) +``` + +### Model Selection Helper + +```python +def select_model(capability: str = "balanced") -> str: + """ + Select appropriate model based on requirements. + + Args: + capability: "maximum" (opus), "balanced" (sonnet), "fast" (haiku) + """ + if capability == "maximum": + return "claude-opus-4-5-20250929" + elif capability == "fast": + return "claude-3-5-haiku-20241022" + else: # balanced + return "claude-sonnet-4-5-20250929" + +model_id = select_model("balanced") +``` + +## See Also + +- [Messages API](./messages.md) - Core message creation +- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md new file mode 100644 index 000000000..76fda7b7a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md @@ -0,0 +1,838 @@ +# Streaming API Reference + +Stream message responses with rich event handling and helper utilities for incremental processing. + +## Stream Message + +```python { .api } +def stream( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + system: str | list[TextBlockParam] = NOT_GIVEN, + temperature: float = NOT_GIVEN, + tools: list[ToolParam] = NOT_GIVEN, + **kwargs +) -> MessageStreamManager: + """ + Stream a message response with helper utilities. + + Returns a context manager that provides streaming with convenient helpers + for text accumulation, event handling, and final message access. + + Parameters: + model: Model identifier (required) + messages: List of conversation messages (required) + max_tokens: Maximum tokens to generate (required) + system: System prompt (string or list with cache control) + temperature: Sampling temperature 0.0-1.0 + tools: Available tools for function calling + **kwargs: All other parameters from messages.create() are supported + + Returns: + MessageStreamManager: Context manager that provides: + - MessageStream for iteration + - Helper methods for text and final message extraction + + Raises: + Same exceptions as messages.create() + + Example: + with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + """ + ... + +async def stream( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + **kwargs +) -> AsyncMessageStreamManager: + """ + Async version of stream. + + Same parameters and behavior as synchronous stream(), but executes + asynchronously. Use with `async with` syntax. + + Returns: + AsyncMessageStreamManager: Async context manager for streaming + """ + ... +``` + +## MessageStream + +```python { .api } +class MessageStream: + """ + Synchronous message stream with rich event handling and helper methods. + + Provides convenient access to streaming events, text deltas, and final message + accumulation. Use via MessageStreamManager context manager. + """ + + def __iter__(self) -> Iterator[MessageStreamEvent]: + """ + Iterate over all stream events. + + Yields: + MessageStreamEvent: Events including message_start, content_block_start, + content_block_delta, content_block_stop, message_delta, message_stop + """ + ... + + def __enter__(self) -> MessageStream: + """Context manager entry. Returns self.""" + ... + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + """Context manager exit. Closes stream and cleans up resources.""" + ... + + def get_final_message(self) -> Message: + """ + Get final accumulated message after stream completes. + + Must be called after iterating through the stream. Returns complete Message + object with all content blocks, usage statistics, and stop reason. + + Returns: + Message: Complete message with all accumulated content + + Raises: + RuntimeError: If called before stream completes + """ + ... + + def get_final_text(self) -> str: + """ + Get final accumulated text from all text blocks. + + Concatenates text from all TextBlock content blocks in the message. + Useful for simple text-only responses. + + Returns: + str: Concatenated text from all text blocks + + Raises: + RuntimeError: If called before stream completes + """ + ... + + @property + def text_stream(self) -> Iterator[str]: + """ + Iterate over text deltas only, filtering out other events. + + Convenient property for streaming text to console or UI. Automatically + extracts text from content_block_delta events. + + Yields: + str: Text delta strings as they arrive + + Example: + for text in stream.text_stream: + print(text, end="", flush=True) + """ + ... + + @property + def current_message_snapshot(self) -> Message: + """ + Get current accumulated message snapshot during streaming. + + Provides partial Message object with content accumulated so far. Useful + for showing partial results or implementing custom UI updates. + + Returns: + Message: Partial message with current content (usage may be incomplete) + """ + ... + +class AsyncMessageStream: + """ + Asynchronous message stream with rich event handling and helper methods. + + Async version of MessageStream. All methods are async and use async iteration. + """ + + def __aiter__(self) -> AsyncIterator[MessageStreamEvent]: + """ + Async iterate over all stream events. + + Yields: + MessageStreamEvent: Events as they arrive from the API + """ + ... + + async def __aenter__(self) -> AsyncMessageStream: + """Async context manager entry. Returns self.""" + ... + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Async context manager exit. Closes stream and cleans up resources.""" + ... + + async def get_final_message(self) -> Message: + """ + Get final accumulated message after stream completes. + + Returns: + Message: Complete message with all accumulated content + """ + ... + + async def get_final_text(self) -> str: + """ + Get final accumulated text from all text blocks. + + Returns: + str: Concatenated text from all text blocks + """ + ... + + @property + def text_stream(self) -> AsyncIterator[str]: + """ + Async iterate over text deltas only. + + Yields: + str: Text delta strings as they arrive + """ + ... + + @property + def current_message_snapshot(self) -> Message: + """ + Get current accumulated message snapshot during streaming. + + Returns: + Message: Partial message with current content + """ + ... +``` + +## MessageStreamManager + +Context manager for streaming messages with helper methods. + +```python { .api } +class MessageStreamManager: + """ + Synchronous context manager for message streaming. + + Provides: + - Context manager protocol + - Access to MessageStream with helpers + """ + def __enter__(self) -> MessageStream: + """Enter context and return stream.""" + ... + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + """Exit context and cleanup.""" + ... + +class AsyncMessageStreamManager: + """ + Asynchronous context manager for message streaming. + + Provides: + - Async context manager protocol + - Access to AsyncMessageStream with helpers + """ + async def __aenter__(self) -> AsyncMessageStream: + """Enter async context and return stream.""" + ... + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Exit async context and cleanup.""" + ... +``` + +## Base Stream Classes + +For advanced use cases requiring raw SSE event access. + +```python { .api } +class Stream(Generic[T]): + """ + Base synchronous stream for raw SSE events. + + Provides: + - Raw event iteration + - Response access + """ + def __iter__(self) -> Iterator[T]: + """Iterate over stream items.""" + ... + + def __enter__(self) -> Stream[T]: + """Context manager entry.""" + ... + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + """Context manager exit.""" + ... + + @property + def response(self) -> httpx.Response: + """Access raw HTTP response.""" + ... + +class AsyncStream(Generic[T]): + """ + Base asynchronous stream for raw SSE events. + + Provides: + - Async raw event iteration + - Response access + """ + def __aiter__(self) -> AsyncIterator[T]: + """Async iterate over stream items.""" + ... + + async def __aenter__(self) -> AsyncStream[T]: + """Async context manager entry.""" + ... + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Async context manager exit.""" + ... + + @property + def response(self) -> httpx.Response: + """Access raw HTTP response.""" + ... +``` + +## Stream Events + +```python { .api } +MessageStreamEvent = Union[ + MessageStartEvent, # Stream started + MessageDeltaEvent, # Usage or stop_reason updated + MessageStopEvent, # Stream completed + ContentBlockStartEvent, # New content block begins + ContentBlockDeltaEvent, # Content delta received + ContentBlockStopEvent, # Content block completed +] +``` + +### Message Lifecycle Events + +```python { .api } +class MessageStartEvent(BaseModel): + """ + Stream started with initial message metadata. + + First event in every stream. Contains message skeleton with empty content. + + Attributes: + type: Always "message_start" + message: Initial Message object with id, role, model, but empty content + """ + type: Literal["message_start"] + message: Message + +class MessageDeltaEvent(BaseModel): + """ + Message metadata updated (stop_reason or usage). + + Sent near end of stream when stop_reason is determined or final usage + statistics are available. + + Attributes: + type: Always "message_delta" + delta: Changed message fields (stop_reason, stop_sequence) + usage: Updated token usage (output_tokens) + """ + type: Literal["message_delta"] + delta: MessageDelta + usage: MessageDeltaUsage + +class MessageDelta(BaseModel): + """ + Changed message fields in MessageDeltaEvent. + + Attributes: + stop_reason: Why generation stopped - "end_turn", "max_tokens", + "stop_sequence", or "tool_use" + stop_sequence: Stop sequence that triggered completion (if applicable) + """ + stop_reason: StopReason | None + stop_sequence: str | None + +class MessageDeltaUsage(BaseModel): + """ + Token usage update in MessageDeltaEvent. + + Attributes: + output_tokens: Total output tokens generated so far + """ + output_tokens: int + +class MessageStopEvent(BaseModel): + """ + Stream completed successfully. + + Final event in every successful stream. After this event, stream is closed + and final message is available via get_final_message(). + + Attributes: + type: Always "message_stop" + """ + type: Literal["message_stop"] +``` + +### Content Block Events + +```python { .api } +class ContentBlockStartEvent(BaseModel): + """ + New content block started in response. + + Sent when Claude begins generating a new content block (text or tool_use). + Contains initial empty block structure. + + Attributes: + type: Always "content_block_start" + index: Zero-based index of this content block in message.content list + content_block: Initial ContentBlock (TextBlock or ToolUseBlock) with + empty/default values + """ + type: Literal["content_block_start"] + index: int + content_block: ContentBlock + +class ContentBlockDeltaEvent(BaseModel): + """ + Content block received incremental update. + + Most frequent event type. Contains incremental content (text deltas or + JSON deltas for tool inputs). + + Attributes: + type: Always "content_block_delta" + index: Zero-based index of content block being updated + delta: Delta content - TextDelta for text blocks, InputJSONDelta for + tool_use blocks + """ + type: Literal["content_block_delta"] + index: int + delta: ContentBlockDelta + +ContentBlockDelta = Union[TextDelta, InputJSONDelta] + +class TextDelta(BaseModel): + """ + Text content delta for TextBlock. + + Attributes: + type: Always "text_delta" + text: Incremental text to append to current text block + """ + type: Literal["text_delta"] + text: str + +class InputJSONDelta(BaseModel): + """ + Tool input JSON delta for ToolUseBlock. + + Attributes: + type: Always "input_json_delta" + partial_json: Incremental JSON string to append. May be incomplete JSON + until block completes. + """ + type: Literal["input_json_delta"] + partial_json: str + +class ContentBlockStopEvent(BaseModel): + """ + Content block completed. + + Sent when a content block finishes. After this event, the content block + at the given index is complete. + + Attributes: + type: Always "content_block_stop" + index: Zero-based index of completed content block + """ + type: Literal["content_block_stop"] + index: int +``` + +### Raw Stream Events + +For advanced use cases requiring access to raw SSE events before parsing. + +```python { .api } +RawMessageStreamEvent = Union[ + RawMessageStartEvent, + RawMessageDeltaEvent, + RawMessageStopEvent, + RawContentBlockStartEvent, + RawContentBlockDeltaEvent, + RawContentBlockStopEvent, +] + +class RawMessageStartEvent(BaseModel): + """Raw message start event from SSE.""" + type: Literal["message_start"] + message: Message + +class RawMessageDeltaEvent(BaseModel): + """Raw message delta event from SSE.""" + type: Literal["message_delta"] + delta: dict[str, Any] + usage: MessageDeltaUsage + +class RawMessageStopEvent(BaseModel): + """Raw message stop event from SSE.""" + type: Literal["message_stop"] + +class RawContentBlockStartEvent(BaseModel): + """Raw content block start from SSE.""" + type: Literal["content_block_start"] + index: int + content_block: dict[str, Any] + +class RawContentBlockDeltaEvent(BaseModel): + """Raw content block delta from SSE.""" + type: Literal["content_block_delta"] + index: int + delta: dict[str, Any] + +class RawContentBlockStopEvent(BaseModel): + """Raw content block stop from SSE.""" + type: Literal["content_block_stop"] + index: int +``` + +## Quick Examples + +### Basic Streaming + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a short story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +print() +``` + +### Stream All Events + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) as stream: + for event in stream: + if event.type == "message_start": + print(f"Message started: {event.message.id}") + elif event.type == "content_block_start": + print(f"Content block {event.index} started") + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + elif event.type == "content_block_stop": + print(f"\nContent block {event.index} stopped") + elif event.type == "message_delta": + print(f"Stop reason: {event.delta.stop_reason}") + elif event.type == "message_stop": + print("Message stopped") +``` + +### Get Final Message + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "What is 2+2?"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + +message = stream.get_final_message() +print(f"\nTotal tokens: {message.usage.output_tokens}") +``` + +### Stream with Tool Use + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[ + { + "name": "get_weather", + "description": "Get weather for a location", + "input_schema": { + "type": "object", + "properties": { + "location": {"type": "string"} + }, + "required": ["location"], + }, + } + ], + messages=[ + {"role": "user", "content": "What's the weather in San Francisco?"} + ], +) as stream: + for event in stream: + if event.type == "content_block_start": + if event.content_block.type == "tool_use": + print(f"Tool call: {event.content_block.name}") + elif event.type == "content_block_delta": + if event.delta.type == "input_json_delta": + print(event.delta.partial_json, end="") + +message = stream.get_final_message() + +# Process tool calls +for block in message.content: + if block.type == "tool_use": + print(f"Tool: {block.name}") + print(f"Input: {block.input}") +``` + +### Async Streaming + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + async with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a haiku"}] + ) as stream: + async for text in stream.text_stream: + print(text, end="", flush=True) + print() + +asyncio.run(main()) +``` + +### Get Final Text Only + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) as stream: + # Consume stream + for _ in stream: + pass + +# Get accumulated text +text = stream.get_final_text() +print(text) +``` + +### Stream with Current Snapshot + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Count to 10"}] +) as stream: + for event in stream: + # Get current accumulated message + current = stream.current_message_snapshot + if current.content: + print(f"Current text length: {len(current.content[0].text)}") +``` + +### Async Event Processing + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def stream_with_events(): + client = AsyncAnthropic() + + async with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) as stream: + async for event in stream: + print(f"Event: {event.type}") + +asyncio.run(stream_with_events()) +``` + +### Error Handling in Streams + +```python +from anthropic import APIError + +try: + with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +except APIError as e: + print(f"Stream error: {e}") +``` + +### Manual Stream Iteration + +```python +stream_manager = client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) + +stream = stream_manager.__enter__() + +try: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) +finally: + stream_manager.__exit__(None, None, None) +``` + +### Streaming with Temperature + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + temperature=0.8, + messages=[{"role": "user", "content": "Write a creative story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +### Stream Multi-Turn Conversation + +```python +conversation = [ + {"role": "user", "content": "Hi, I'm Alice"}, +] + +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=conversation, +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + + message = stream.get_final_message() + +# Add to conversation +conversation.append({ + "role": "assistant", + "content": message.content, +}) +conversation.append({ + "role": "user", + "content": "What's my name?", +}) + +# Continue streaming +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=conversation, +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +### Access Raw HTTP Response + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) as stream: + # Access underlying HTTP response + print(f"Request ID: {stream.response.headers.get('request-id')}") + + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +### Concurrent Async Streams + +```python +async def stream_multiple(): + client = AsyncAnthropic() + + async def stream_one(prompt: str): + async with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": prompt}], + ) as stream: + text = await stream.get_final_text() + return text + + results = await asyncio.gather( + stream_one("What is 2+2?"), + stream_one("What is the capital of France?"), + stream_one("What is Python?"), + ) + + for result in results: + print(result) + print("---") + +asyncio.run(stream_multiple()) +``` + +### Track Token Usage During Streaming + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a long essay"}] +) as stream: + for event in stream: + if event.type == "message_delta": + print(f"\nTokens so far: {event.usage.output_tokens}") + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) +``` + +## See Also + +- [Messages API](./messages.md) - Core message creation +- [Streaming Guide](../guides/streaming-guide.md) - Advanced streaming patterns +- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md new file mode 100644 index 000000000..f51e2e00d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md @@ -0,0 +1,423 @@ +# Tool Use API Reference + +Define and use tools (function calling) with automatic schema generation from Python functions. + +## Tool Decorators + +```python { .api } +def beta_tool(func: Callable) -> BetaFunctionTool: + """ + Decorator to create a synchronous tool from Python function. + + Automatically generates JSON schema from function signature, type hints, + and docstring. Supports standard Python types, Optional, Union, List, Dict. + + Parameters: + func: Python function to convert to tool. Must have type hints and docstring. + + Returns: + BetaFunctionTool: Wrapper with auto-generated schema and callable interface + + Example: + @beta_tool + def get_weather(location: str, unit: str = "fahrenheit") -> dict: + '''Get weather for location. + + Args: + location: City and state + unit: Temperature unit + ''' + return {"temp": 72, "condition": "sunny"} + """ + ... + +def beta_async_tool(func: Callable) -> BetaAsyncFunctionTool: + """ + Decorator to create an asynchronous tool from Python async function. + + Automatically generates JSON schema from function signature, type hints, + and docstring. Use for tools that perform async I/O operations. + + Parameters: + func: Python async function to convert to tool. Must have type hints and docstring. + + Returns: + BetaAsyncFunctionTool: Wrapper with auto-generated schema and async callable interface + + Example: + @beta_async_tool + async def fetch_url(url: str) -> str: + '''Fetch content from URL. + + Args: + url: URL to fetch + ''' + async with httpx.AsyncClient() as client: + response = await client.get(url) + return response.text + """ + ... +``` + +## Tool Runner + +```python { .api } +def tool_runner( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + tools: list[ToolParam | BetaFunctionTool], + tool_choice: ToolChoice = NOT_GIVEN, + **kwargs +) -> Iterator[BetaMessage]: + """ + Run message with automatic tool execution and conversation management. + + Automatically handles the agentic loop: sends message to Claude, executes any + tool calls, sends results back, and continues until Claude responds without + tool use (stop_reason == "end_turn"). + + Parameters: + model: Model identifier (required) + messages: Initial conversation messages (required) + max_tokens: Maximum tokens per Claude response (required) + tools: List of available tools - ToolParam dicts or BetaFunctionTool/BetaAsyncFunctionTool + decorated functions + tool_choice: Control tool selection behavior - "auto" (default), "any", "none", + or specific tool name + **kwargs: Additional parameters passed to messages.create() (system, temperature, etc.) + + Yields: + BetaMessage: Message after each turn in the conversation loop. Final message + will have stop_reason "end_turn" when conversation completes. + + Raises: + Same exceptions as messages.create() + ToolExecutionError: If tool execution fails and error handling doesn't catch it + + Example: + for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather, search_db], + messages=[{"role": "user", "content": "What's the weather?"}] + ): + if message.stop_reason == "end_turn": + print(message.content[0].text) + """ + ... + +async def tool_runner( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + tools: list[ToolParam | BetaAsyncFunctionTool], + tool_choice: ToolChoice = NOT_GIVEN, + **kwargs +) -> AsyncIterator[BetaMessage]: + """ + Async version of tool_runner. + + Same parameters and behavior as synchronous tool_runner(), but executes + asynchronously and supports async tools (BetaAsyncFunctionTool). + + Yields: + BetaMessage: Message after each turn in the conversation loop + """ + ... +``` + +## Tool Types + +### ToolParam + +```python { .api } +class ToolParam(TypedDict): + """ + Manual tool definition without decorator. + + Fields: + name: Tool name (alphanumeric + underscores only). Used by Claude to identify tool. + description: Clear description of what the tool does. Claude uses this to decide + when to use the tool. + input_schema: JSON Schema (draft 2020-12) defining tool parameters. Use standard + JSON Schema types: object, string, number, boolean, array, etc. + cache_control: Optional prompt caching. Use {"type": "ephemeral"} to cache tool + definition across requests. + """ + name: str + description: str + input_schema: dict[str, Any] + cache_control: NotRequired[CacheControlEphemeral] +``` + +### ToolChoice + +```python { .api } +ToolChoice = Union[ + ToolChoiceAuto, # Let Claude decide whether to use tools (default) + ToolChoiceAny, # Force Claude to use at least one tool + ToolChoiceNone, # Disable all tool use for this request + ToolChoiceTool, # Force Claude to use specific tool by name +] + +class ToolChoiceAuto(TypedDict): + """ + Let Claude decide whether to use tools based on conversation context. + + Fields: + type: Always "auto" + disable_parallel_tool_use: Set to True to force sequential tool calls. + Default is False (parallel calls allowed). + """ + type: Literal["auto"] + disable_parallel_tool_use: NotRequired[bool] + +class ToolChoiceAny(TypedDict): + """ + Force Claude to use at least one tool (any tool from available tools). + + Useful when you want to ensure Claude performs an action rather than just + responding with text. + + Fields: + type: Always "any" + disable_parallel_tool_use: Set to True to force sequential tool calls + """ + type: Literal["any"] + disable_parallel_tool_use: NotRequired[bool] + +class ToolChoiceNone(TypedDict): + """ + Disable all tool use for this request. + + Claude will respond with text only, even if tools are provided. Useful for + forcing text responses in multi-turn conversations. + + Fields: + type: Always "none" + """ + type: Literal["none"] + +class ToolChoiceTool(TypedDict): + """ + Force Claude to use a specific tool by name. + + Claude will always use this tool in its first response. Useful for + deterministic workflows. + + Fields: + type: Always "tool" + name: Exact name of tool to use (must match tool in tools list) + disable_parallel_tool_use: Set to True to force only this tool call + """ + type: Literal["tool"] + name: str + disable_parallel_tool_use: NotRequired[bool] +``` + +### Tool Function Classes + +```python { .api } +class BetaFunctionTool: + """ + Synchronous function tool wrapper created by @beta_tool decorator. + + Attributes: + name: Tool name derived from function name + description: Tool description from function docstring + input_schema: Auto-generated JSON Schema from function signature + func: Underlying Python function + """ + name: str + description: str + input_schema: dict[str, Any] + func: Callable + + def __call__(self, **kwargs) -> Any: + """Execute tool with parameters.""" + ... + + def to_param(self) -> ToolParam: + """Convert to ToolParam dict for API calls.""" + ... + +class BetaAsyncFunctionTool: + """ + Asynchronous function tool wrapper created by @beta_async_tool decorator. + + Attributes: + name: Tool name derived from function name + description: Tool description from function docstring + input_schema: Auto-generated JSON Schema from function signature + func: Underlying Python async function + """ + name: str + description: str + input_schema: dict[str, Any] + func: Callable + + async def __call__(self, **kwargs) -> Any: + """Execute async tool with parameters.""" + ... + + def to_param(self) -> ToolParam: + """Convert to ToolParam dict for API calls.""" + ... +``` + +## Quick Examples + +### Manual Tool Definition + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[{ + "name": "get_weather", + "description": "Get weather for a location", + "input_schema": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "City and state"} + }, + "required": ["location"] + } + }], + messages=[{"role": "user", "content": "What's the weather in SF?"}] +) + +for block in message.content: + if block.type == "tool_use": + print(f"Tool: {block.name}, Input: {block.input}") +``` + +### Using Tool Decorator + +```python +from anthropic import beta_tool + +@beta_tool +def get_weather(location: str, unit: str = "fahrenheit") -> dict: + """ + Get weather for a location. + + Args: + location: City and state, e.g. San Francisco, CA + unit: Temperature unit (celsius or fahrenheit) + """ + return {"location": location, "temperature": 72, "unit": unit, "condition": "sunny"} + +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather], + messages=[{"role": "user", "content": "What's the weather in NYC?"}] +) +``` + +### Tool Runner with Auto-Execution + +```python +@beta_tool +def get_weather(location: str) -> dict: + """Get weather for location.""" + return {"temp": 72, "condition": "sunny"} + +@beta_tool +def search_database(query: str) -> list: + """Search database.""" + return [{"id": 1, "name": "Result 1"}] + +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather, search_database], + messages=[{"role": "user", "content": "What's the weather in SF?"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +### Handle Tool Call Manually + +```python +# First request - Claude requests tool use +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + messages=[{"role": "user", "content": "What's the weather in Paris?"}] +) + +# Extract and execute tool call +tool_use = next(block for block in message.content if block.type == "tool_use") +weather_data = get_weather(location=tool_use.input["location"]) + +# Send result back +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + messages=[ + {"role": "user", "content": "What's the weather in Paris?"}, + {"role": "assistant", "content": message.content}, + { + "role": "user", + "content": [{ + "type": "tool_result", + "tool_use_id": tool_use.id, + "content": str(weather_data) + }] + } + ] +) +``` + +### Force Tool Use + +```python +# Force any tool +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "any"}, + messages=[{"role": "user", "content": "Hello"}] +) + +# Force specific tool +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "tool", "name": "get_weather"}, + messages=[{"role": "user", "content": "Hello"}] +) +``` + +### Disable Parallel Tool Use + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={ + "type": "auto", + "disable_parallel_tool_use": True + }, + messages=[{"role": "user", "content": "Get weather for NYC and LA"}] +) +``` + +## See Also + +- [Messages API](./messages.md) - Core message creation +- [Tool Usage Guide](../guides/tool-usage.md) - Advanced tool patterns +- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md new file mode 100644 index 000000000..8de22d8eb --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md @@ -0,0 +1,611 @@ +# Beta Message Batches + +Process multiple beta message requests in batch mode with support for all beta features including thinking, citations, web search, code execution, and more. + +## Overview + +Beta message batches extend standard message batches with support for beta features. They provide: +- 50% cost reduction compared to standard API +- Support for all beta features (thinking, citations, web search, etc.) +- Same interface as standard batches +- Asynchronous processing of thousands of requests + +## API Reference + +### Create Batch + +```python { .api } +def create( + self, + *, + requests: Iterable[Request], + betas: list[AnthropicBetaParam] = NOT_GIVEN, + **kwargs +) -> BetaMessageBatch: + """ + Create a batch of beta message requests. + + Parameters: + requests: List of beta message creation requests + Each request includes custom_id and params dict + betas: Additional beta features to enable beyond default batch support + + Returns: + BetaMessageBatch with batch ID and processing status + """ + ... + +async def create( + self, + **kwargs +) -> BetaMessageBatch: + """Async version of create.""" + ... +``` + +### Retrieve Batch + +```python { .api } +def retrieve( + self, + message_batch_id: str, + *, + betas: list[AnthropicBetaParam] = NOT_GIVEN, + **kwargs +) -> BetaMessageBatch: + """ + Retrieve beta message batch status and metadata. + + Parameters: + message_batch_id: ID of the batch to retrieve + betas: Optional beta features header + + Returns: + BetaMessageBatch with current processing status + """ + ... + +async def retrieve( + self, + message_batch_id: str, + **kwargs +) -> BetaMessageBatch: + """Async version of retrieve.""" + ... +``` + +### List Batches + +```python { .api } +def list( + self, + *, + after_id: str = NOT_GIVEN, + before_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + betas: list[AnthropicBetaParam] = NOT_GIVEN, + **kwargs +) -> SyncPage[BetaMessageBatch]: + """ + List beta message batches with pagination. + + Parameters: + after_id: Return batches after this ID + before_id: Return batches before this ID + limit: Maximum number of batches to return (1-1000, default 20) + betas: Optional beta features header + + Returns: + Paginated list of BetaMessageBatch objects + """ + ... + +def list( + self, + **kwargs +) -> AsyncPaginator[BetaMessageBatch, AsyncPage[BetaMessageBatch]]: + """Async version of list.""" + ... +``` + +### Cancel Batch + +```python { .api } +def cancel( + self, + message_batch_id: str, + *, + betas: list[AnthropicBetaParam] = NOT_GIVEN, + **kwargs +) -> BetaMessageBatch: + """ + Cancel a beta message batch before processing completes. + + Parameters: + message_batch_id: ID of the batch to cancel + betas: Optional beta features header + + Returns: + BetaMessageBatch with canceling status + """ + ... + +async def cancel( + self, + message_batch_id: str, + **kwargs +) -> BetaMessageBatch: + """Async version of cancel.""" + ... +``` + +### Delete Batch + +```python { .api } +def delete( + self, + message_batch_id: str, + *, + betas: list[AnthropicBetaParam] = NOT_GIVEN, + **kwargs +) -> BetaDeletedMessageBatch: + """ + Delete a completed beta message batch. + + Batches must be finished processing before deletion. + Cancel in-progress batches first if needed. + + Parameters: + message_batch_id: ID of the batch to delete + betas: Optional beta features header + + Returns: + BetaDeletedMessageBatch confirming deletion + """ + ... + +async def delete( + self, + message_batch_id: str, + **kwargs +) -> BetaDeletedMessageBatch: + """Async version of delete.""" + ... +``` + +### Get Results + +```python { .api } +def results( + self, + message_batch_id: str, + *, + betas: list[AnthropicBetaParam] = NOT_GIVEN, + **kwargs +) -> JSONLDecoder[BetaMessageBatchIndividualResponse]: + """ + Stream beta message batch results as JSONL. + + Each line contains one request's result with custom_id for matching. + Results order is not guaranteed to match request order. + + Parameters: + message_batch_id: ID of the batch + betas: Optional beta features header + + Returns: + JSONLDecoder streaming individual responses + """ + ... + +async def results( + self, + message_batch_id: str, + **kwargs +) -> AsyncJSONLDecoder[BetaMessageBatchIndividualResponse]: + """Async version of results.""" + ... +``` + +## Examples + +### Basic Batch with Beta Features + +```python +batch = client.beta.messages.batches.create( + requests=[ + { + "custom_id": "request-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "thinking": {"type": "enabled"}, + "messages": [ + {"role": "user", "content": "Solve this problem..."} + ], + }, + }, + { + "custom_id": "request-2", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "web_search": {"type": "enabled"}, + "messages": [ + {"role": "user", "content": "What are the latest news?"} + ], + }, + } + ] +) + +print(f"Batch ID: {batch.id}") +``` + +### Wait for Completion and Get Results + +```python +import time + +# Wait for completion +while True: + batch = client.beta.messages.batches.retrieve(batch.id) + if batch.processing_status == "ended": + break + print(f"Processing: {batch.request_counts.processing} requests remaining") + time.sleep(60) + +# Process results +for response in client.beta.messages.batches.results(batch.id): + if response.result.type == "succeeded": + print(f"{response.custom_id}: Success") + # Access beta feature content + for block in response.result.message.content: + if block.type == "thinking": + print(f" Reasoning: {block.thinking[:100]}...") + elif block.type == "text": + print(f" Response: {block.text[:100]}...") + elif response.result.type == "errored": + print(f"{response.custom_id}: Error - {response.result.error.message}") +``` + +### Mixed Beta Features in Batch + +```python +# Create batch with different features per request +requests = [ + { + "custom_id": "thinking-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 4096, + "thinking": {"type": "enabled", "budget_tokens": 2000}, + "messages": [{"role": "user", "content": "Complex reasoning task"}] + } + }, + { + "custom_id": "citations-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "citations": {"type": "enabled"}, + "messages": [{ + "role": "user", + "content": [ + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, + {"type": "text", "text": "Summarize with citations"} + ] + }] + } + }, + { + "custom_id": "web-search-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "web_search": {"type": "enabled"}, + "messages": [{"role": "user", "content": "Latest AI news"}] + } + }, + { + "custom_id": "combined-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 4096, + "thinking": {"type": "enabled"}, + "web_search": {"type": "enabled"}, + "code_execution": {"type": "enabled"}, + "messages": [{"role": "user", "content": "Research and code solution"}] + } + } +] + +batch = client.beta.messages.batches.create(requests=requests) +``` + +### Large-Scale Beta Batch + +```python +# Process thousands of requests with beta features +requests = [] +for i in range(5000): + requests.append({ + "custom_id": f"request-{i}", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "thinking": {"type": "enabled"}, + "messages": [{"role": "user", "content": f"Task {i}: ..."}] + } + }) + +batch = client.beta.messages.batches.create(requests=requests) +print(f"Created batch {batch.id} with {len(requests)} requests") + +# Monitor progress +while True: + batch = client.beta.messages.batches.retrieve(batch.id) + completed = batch.request_counts.succeeded + batch.request_counts.errored + total = completed + batch.request_counts.processing + progress = (completed / total * 100) if total > 0 else 0 + + print(f"Progress: {progress:.1f}% ({completed}/{total})") + + if batch.processing_status == "ended": + break + time.sleep(60) + +# Process all results +results_by_id = {} +for response in client.beta.messages.batches.results(batch.id): + results_by_id[response.custom_id] = response.result + +print(f"Processed {len(results_by_id)} results") +``` + +### Async Batch Operations + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + # Create batch + batch = await client.beta.messages.batches.create( + requests=[ + { + "custom_id": "async-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "thinking": {"type": "enabled"}, + "messages": [{"role": "user", "content": "Task 1"}] + } + }, + { + "custom_id": "async-2", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "web_search": {"type": "enabled"}, + "messages": [{"role": "user", "content": "Task 2"}] + } + } + ] + ) + + # Poll until complete + while True: + batch = await client.beta.messages.batches.retrieve(batch.id) + if batch.processing_status == "ended": + break + await asyncio.sleep(60) + + # Get results + results = [] + async for response in client.beta.messages.batches.results(batch.id): + results.append(response) + + return results + +results = asyncio.run(main()) +``` + +### Cancel Batch + +```python +# Cancel batch in progress +batch = client.beta.messages.batches.cancel("batch_abc123") +print(f"Status: {batch.processing_status}") # "canceling" + +# Wait for cancellation to complete +while True: + batch = client.beta.messages.batches.retrieve(batch.id) + if batch.processing_status == "ended": + break + time.sleep(30) + +print(f"Canceled: {batch.request_counts.canceled} requests") +``` + +### Delete Batch + +```python +# Delete completed batch +deleted = client.beta.messages.batches.delete("batch_abc123") +print(f"Deleted batch: {deleted.id}") +``` + +### List All Batches + +```python +# List recent batches +for batch in client.beta.messages.batches.list(limit=10): + print(f"{batch.id}: {batch.processing_status}") + print(f" Succeeded: {batch.request_counts.succeeded}") + print(f" Errored: {batch.request_counts.errored}") + print(f" Processing: {batch.request_counts.processing}") +``` + +### Error Handling + +```python +from anthropic import APIError + +try: + batch = client.beta.messages.batches.create(requests=[...]) +except APIError as e: + print(f"Failed to create batch: {e.message}") + +# Process results with error handling +for response in client.beta.messages.batches.results(batch.id): + if response.result.type == "succeeded": + try: + # Process successful result + message = response.result.message + ... + except Exception as e: + print(f"Error processing {response.custom_id}: {e}") + elif response.result.type == "errored": + print(f"Request {response.custom_id} failed: {response.result.error.message}") + elif response.result.type == "canceled": + print(f"Request {response.custom_id} was canceled") + elif response.result.type == "expired": + print(f"Request {response.custom_id} expired") +``` + +## Best Practices + +### 1. Use Meaningful Custom IDs + +```python +# Good - descriptive IDs +custom_id = f"user-{user_id}-task-{task_id}-{timestamp}" + +# Bad - generic IDs +custom_id = f"request-{i}" +``` + +### 2. Batch Similar Requests + +Group requests with similar beta features: + +```python +# Batch 1: Thinking-heavy tasks +thinking_requests = [...] + +# Batch 2: Web search tasks +search_requests = [...] + +batch1 = client.beta.messages.batches.create(requests=thinking_requests) +batch2 = client.beta.messages.batches.create(requests=search_requests) +``` + +### 3. Monitor Progress + +```python +def monitor_batch(batch_id): + """Monitor batch with progress updates.""" + last_progress = 0 + + while True: + batch = client.beta.messages.batches.retrieve(batch_id) + + total = sum([ + batch.request_counts.processing, + batch.request_counts.succeeded, + batch.request_counts.errored, + batch.request_counts.canceled + ]) + completed = batch.request_counts.succeeded + batch.request_counts.errored + progress = (completed / total * 100) if total > 0 else 0 + + if progress > last_progress + 5: # Log every 5% + print(f"Progress: {progress:.1f}%") + last_progress = progress + + if batch.processing_status == "ended": + break + + time.sleep(60) + + return batch + +batch = monitor_batch("batch_abc123") +``` + +### 4. Handle All Result Types + +```python +success_count = 0 +error_count = 0 +canceled_count = 0 +expired_count = 0 + +for response in client.beta.messages.batches.results(batch.id): + if response.result.type == "succeeded": + success_count += 1 + process_success(response) + elif response.result.type == "errored": + error_count += 1 + log_error(response) + elif response.result.type == "canceled": + canceled_count += 1 + elif response.result.type == "expired": + expired_count += 1 + +print(f"Results: {success_count} success, {error_count} errors, {canceled_count} canceled, {expired_count} expired") +``` + +### 5. Batch Size Considerations + +- Maximum 10,000 requests per batch +- Consider splitting very large workloads +- Balance batch size with monitoring needs + +```python +def create_batches_chunked(requests, chunk_size=5000): + """Split large request list into multiple batches.""" + batches = [] + + for i in range(0, len(requests), chunk_size): + chunk = requests[i:i+chunk_size] + batch = client.beta.messages.batches.create(requests=chunk) + batches.append(batch) + + return batches + +all_requests = [...] # 20,000 requests +batches = create_batches_chunked(all_requests) +``` + +## Limitations and Considerations + +### Beta Feature Support +- All beta features supported in batches +- Same limitations apply as in standard requests +- Token budgets apply per request + +### Processing Time +- Batches typically complete within 24 hours +- Large batches may take longer +- Complexity affects processing time + +### Cost Optimization +- 50% cost reduction vs standard API +- Beta features may have additional costs +- Calculate total cost including beta feature usage + +### Rate Limits +- Batch requests don't count against rate limits +- Batch creation has its own limits +- Monitor batch creation rate + +## See Also + +- [Beta Overview](./index.md) - Overview of all beta features +- [Message Features](./message-features.md) - Beta feature documentation +- [Standard Batches](../api/batches.md) - Standard batch processing +- [Messages API](../api/messages.md) - Core message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md new file mode 100644 index 000000000..0dc9a3300 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md @@ -0,0 +1,669 @@ +# Beta Files API + +Upload and manage files for use in conversations and batch processing. + +## Overview + +The Files API enables you to upload, manage, and download files for use with Claude. Files can be used for document analysis, batch processing input, or general user data. + +## Key Features + +- Upload files with specified purpose (batch or user_data) +- Retrieve file metadata +- List uploaded files with pagination +- Download file content +- Delete files when no longer needed + +## Files API + +### Upload File + +```python { .api } +def upload( + self, + *, + file: FileTypes, + purpose: Literal["batch", "user_data"], + **kwargs +) -> FileMetadata: + """ + Upload file. + + Parameters: + file: File to upload (bytes, file path, or file object) + purpose: Purpose of file + "batch" - For batch processing requests/results + "user_data" - For general document analysis and user data + + Returns: + FileMetadata with file ID, size, created timestamp, and purpose + """ + ... +``` + +### Retrieve File Metadata + +```python { .api } +def retrieve( + self, + file_id: str, + **kwargs +) -> FileMetadata: + """ + Get file metadata. + + Parameters: + file_id: Unique identifier for the file + + Returns: + FileMetadata with file details (ID, filename, size, purpose, created_at) + """ + ... +``` + +### List Files + +```python { .api } +def list( + self, + *, + before_id: str = NOT_GIVEN, + after_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + **kwargs +) -> SyncPage[FileMetadata]: + """ + List uploaded files with pagination. + + Parameters: + before_id: Return files before this ID (for reverse pagination) + after_id: Return files after this ID (for forward pagination) + limit: Maximum number of files to return (default varies by API) + + Returns: + SyncPage[FileMetadata] with paginated file list + """ + ... +``` + +### Delete File + +```python { .api } +def delete( + self, + file_id: str, + **kwargs +) -> DeletedFile: + """ + Delete a file. + + Parameters: + file_id: Unique identifier for the file to delete + + Returns: + DeletedFile confirming deletion + """ + ... +``` + +### Download File + +```python { .api } +def download( + self, + file_id: str, + **kwargs +) -> bytes: + """ + Download file content. + + Parameters: + file_id: Unique identifier for the file + + Returns: + bytes: Raw file content + """ + ... +``` + +## Examples + +### Upload File from Path + +```python +from anthropic import Anthropic +from anthropic._utils import file_from_path + +client = Anthropic() + +# Upload PDF for document analysis +file = file_from_path("document.pdf") +uploaded = client.beta.files.upload( + file=file, + purpose="user_data" +) + +print(f"Uploaded: {uploaded.id}") +print(f"Filename: {uploaded.filename}") +print(f"Size: {uploaded.size} bytes") +print(f"Created: {uploaded.created_at}") +``` + +### Upload File from Bytes + +```python +# Upload file from bytes +file_content = b"Sample document content..." +uploaded = client.beta.files.upload( + file=("document.txt", file_content), + purpose="user_data" +) + +print(f"Uploaded file ID: {uploaded.id}") +``` + +### Upload for Batch Processing + +```python +import json + +# Prepare batch requests +batch_requests = [ + { + "custom_id": "request-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "Hello"}] + } + } +] + +# Convert to JSONL format +jsonl_content = "\n".join(json.dumps(req) for req in batch_requests) + +# Upload batch file +batch_file = client.beta.files.upload( + file=("batch_requests.jsonl", jsonl_content.encode()), + purpose="batch" +) + +print(f"Batch file uploaded: {batch_file.id}") +``` + +### Retrieve File Metadata + +```python +# Get file details +file_metadata = client.beta.files.retrieve("file_abc123") + +print(f"ID: {file_metadata.id}") +print(f"Filename: {file_metadata.filename}") +print(f"Size: {file_metadata.size} bytes") +print(f"Purpose: {file_metadata.purpose}") +print(f"Created: {file_metadata.created_at}") +``` + +### List All Files + +```python +# List all uploaded files +for file in client.beta.files.list(): + print(f"{file.filename} ({file.id}) - {file.purpose}") +``` + +### List Files with Pagination + +```python +# List files with limit +page1 = client.beta.files.list(limit=10) +for file in page1: + print(f"File: {file.filename}") + +# Get next page +if page1.has_next_page(): + page2 = client.beta.files.list(limit=10, after_id=page1.data[-1].id) + for file in page2: + print(f"File: {file.filename}") + +# List files in reverse +recent_files = client.beta.files.list(limit=5) +older_files = client.beta.files.list( + limit=5, + before_id=recent_files.data[0].id +) +``` + +### Download File + +```python +# Download file content +file_id = "file_abc123" +content = client.beta.files.download(file_id) + +# Save to disk +with open("downloaded_file.pdf", "wb") as f: + f.write(content) + +print(f"Downloaded {len(content)} bytes") +``` + +### Delete File + +```python +# Delete file +deleted = client.beta.files.delete("file_abc123") +print(f"Deleted file: {deleted.id}") +print(f"Deleted: {deleted.deleted}") +``` + +### Use File in Message + +```python +# Upload document +file = file_from_path("research_paper.pdf") +uploaded = client.beta.files.upload( + file=file, + purpose="user_data" +) + +# Use file in message with citations +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + citations={"type": "enabled"}, + messages=[ + { + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": uploaded.id # Use file ID + } + }, + { + "type": "text", + "text": "Summarize this document with citations" + } + ] + } + ] +) + +# Process citations +for block in message.content: + if hasattr(block, 'citations'): + for citation in block.citations: + print(f"Citation: {citation.cited_text}") +``` + +### Manage Multiple Files + +```python +from anthropic._utils import file_from_path + +# Upload multiple files +files_to_upload = [ + "document1.pdf", + "document2.pdf", + "data.csv" +] + +uploaded_files = [] +for filepath in files_to_upload: + file = file_from_path(filepath) + uploaded = client.beta.files.upload( + file=file, + purpose="user_data" + ) + uploaded_files.append(uploaded) + print(f"Uploaded {uploaded.filename}: {uploaded.id}") + +# Track uploaded files +file_ids = [f.id for f in uploaded_files] +print(f"Uploaded {len(file_ids)} files: {file_ids}") + +# Later: clean up files +for file_id in file_ids: + client.beta.files.delete(file_id) + print(f"Deleted {file_id}") +``` + +### Async File Operations + +```python +import asyncio +from anthropic import AsyncAnthropic +from anthropic._utils import file_from_path + +async def main(): + client = AsyncAnthropic() + + # Upload file + file = file_from_path("document.pdf") + uploaded = await client.beta.files.upload( + file=file, + purpose="user_data" + ) + print(f"Uploaded: {uploaded.id}") + + # List files + files = await client.beta.files.list(limit=10) + async for file in files: + print(f"File: {file.filename}") + + # Download file + content = await client.beta.files.download(uploaded.id) + print(f"Downloaded {len(content)} bytes") + + # Delete file + deleted = await client.beta.files.delete(uploaded.id) + print(f"Deleted: {deleted.id}") + +asyncio.run(main()) +``` + +### Error Handling + +```python +from anthropic import APIError, NotFoundError, BadRequestError + +# Upload with error handling +try: + file = file_from_path("large_document.pdf") + uploaded = client.beta.files.upload( + file=file, + purpose="user_data" + ) + print(f"Uploaded: {uploaded.id}") +except BadRequestError as e: + if "size" in str(e).lower(): + print(f"File too large: {e.message}") + else: + print(f"Invalid file: {e.message}") +except APIError as e: + print(f"Upload failed: {e.message}") + +# Download with error handling +try: + content = client.beta.files.download("file_abc123") + with open("output.pdf", "wb") as f: + f.write(content) +except NotFoundError: + print("File not found") +except APIError as e: + print(f"Download failed: {e.message}") + +# Delete with validation +file_id = "file_abc123" +try: + # Check if file exists + file_metadata = client.beta.files.retrieve(file_id) + print(f"Deleting {file_metadata.filename}") + + # Delete file + deleted = client.beta.files.delete(file_id) + print(f"Deleted: {deleted.deleted}") +except NotFoundError: + print(f"File {file_id} not found") +except APIError as e: + print(f"Deletion failed: {e.message}") +``` + +### File Lifecycle Management + +```python +class FileManager: + """Manage file uploads with automatic cleanup.""" + + def __init__(self, client): + self.client = client + self.uploaded_files = [] + + def upload(self, filepath, purpose="user_data"): + """Upload file and track it.""" + file = file_from_path(filepath) + uploaded = self.client.beta.files.upload( + file=file, + purpose=purpose + ) + self.uploaded_files.append(uploaded.id) + return uploaded + + def cleanup(self): + """Delete all tracked files.""" + for file_id in self.uploaded_files: + try: + self.client.beta.files.delete(file_id) + print(f"Deleted {file_id}") + except APIError as e: + print(f"Failed to delete {file_id}: {e.message}") + self.uploaded_files.clear() + +# Usage +manager = FileManager(client) + +try: + # Upload files + file1 = manager.upload("doc1.pdf") + file2 = manager.upload("doc2.pdf") + + # Use files... + print(f"Using files: {file1.id}, {file2.id}") + +finally: + # Always cleanup + manager.cleanup() +``` + +### Check File Before Use + +```python +def safe_file_operation(file_id): + """Safely perform operations on a file.""" + try: + # Verify file exists + metadata = client.beta.files.retrieve(file_id) + + # Check file properties + if metadata.purpose != "user_data": + print(f"Warning: File has purpose '{metadata.purpose}'") + + # Download if needed + content = client.beta.files.download(file_id) + print(f"File size: {len(content)} bytes") + + return content + + except NotFoundError: + print(f"File {file_id} not found") + return None + except APIError as e: + print(f"Error: {e.message}") + return None + +# Use safely +content = safe_file_operation("file_abc123") +if content: + # Process content + pass +``` + +## File Purposes + +### batch + +Used for batch processing operations: +- Batch request files (JSONL format) +- Batch result files +- Structured data for bulk operations + +Example: +```python +batch_file = client.beta.files.upload( + file=("requests.jsonl", jsonl_data), + purpose="batch" +) +``` + +### user_data + +Used for general document analysis and conversation: +- PDF documents +- Text files +- Images +- CSV data +- Any user-provided content + +Example: +```python +doc_file = client.beta.files.upload( + file=file_from_path("document.pdf"), + purpose="user_data" +) +``` + +## Best Practices + +### 1. Use Appropriate Purpose + +Choose the right purpose for your use case: +```python +# For batch processing +client.beta.files.upload(file=batch_data, purpose="batch") + +# For document analysis +client.beta.files.upload(file=document, purpose="user_data") +``` + +### 2. Track Uploaded Files + +Keep track of file IDs for cleanup: +```python +uploaded_ids = [] +for doc in documents: + uploaded = client.beta.files.upload(file=doc, purpose="user_data") + uploaded_ids.append(uploaded.id) + +# Later cleanup +for file_id in uploaded_ids: + client.beta.files.delete(file_id) +``` + +### 3. Handle Errors Gracefully + +Always handle file operation errors: +```python +try: + uploaded = client.beta.files.upload(file=large_file, purpose="user_data") +except BadRequestError as e: + # Handle validation errors (size, format, etc.) + handle_validation_error(e) +except APIError as e: + # Handle other API errors + handle_api_error(e) +``` + +### 4. Clean Up Unused Files + +Delete files when no longer needed: +```python +# After processing +client.beta.files.delete(file_id) +``` + +### 5. Use file_from_path Helper + +Leverage the SDK helper for file uploads: +```python +from anthropic._utils import file_from_path + +# Automatically handles file reading and metadata +file = file_from_path("document.pdf") +uploaded = client.beta.files.upload(file=file, purpose="user_data") +``` + +### 6. Validate Before Upload + +Check file properties before uploading: +```python +import os + +def validate_and_upload(filepath, max_size_mb=10): + """Validate file before upload.""" + if not os.path.exists(filepath): + raise ValueError(f"File not found: {filepath}") + + size_mb = os.path.getsize(filepath) / (1024 * 1024) + if size_mb > max_size_mb: + raise ValueError(f"File too large: {size_mb:.1f}MB (max {max_size_mb}MB)") + + return client.beta.files.upload( + file=file_from_path(filepath), + purpose="user_data" + ) +``` + +### 7. Pagination for Large Lists + +Use pagination for many files: +```python +def list_all_files(client): + """List all files with pagination.""" + all_files = [] + after_id = None + + while True: + page = client.beta.files.list(limit=100, after_id=after_id) + all_files.extend(page.data) + + if not page.has_next_page(): + break + + after_id = page.data[-1].id + + return all_files +``` + +## Limitations and Considerations + +### File Size Limits + +- Check API documentation for current file size limits +- Limits may vary by purpose (batch vs user_data) +- Consider splitting large files if possible + +### Supported Formats + +- PDFs (for document analysis) +- Text files (plain text, markdown, etc.) +- Images (PNG, JPEG, etc.) +- JSONL (for batch processing) +- CSV and other structured data formats + +### File Retention + +- Files persist until explicitly deleted +- No automatic cleanup +- Monitor storage usage + +### Purpose-Specific Constraints + +- batch: Must be valid JSONL format for batch requests +- user_data: General content, validated by API + +### Rate Limits + +- File upload operations count against rate limits +- Large files may take longer to process +- Consider rate limiting for bulk uploads + +## See Also + +- [Beta Overview](./index.md) - Overview of all beta features +- [Skills API](./skills.md) - Create and manage reusable skills +- [Beta Batches](./batches.md) - Batch processing with files +- [Message Features](./message-features.md) - Using files in messages with citations diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md new file mode 100644 index 000000000..907b45c2c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md @@ -0,0 +1,358 @@ +# Beta Features Overview + +Access experimental capabilities through the `client.beta` namespace including extended thinking, citations, web search, code execution, computer use, and more. + +## What Are Beta Features? + +Beta features provide experimental functionality that may change in future releases. They enable advanced capabilities beyond standard message creation. + +## Available Beta Features + +### Message Enhancement Features + +Add advanced capabilities to message creation: + +- **[Extended Thinking](./message-features.md#extended-thinking)** - Enable Claude to show detailed reasoning with configurable token budget +- **[Citations](./message-features.md#citations)** - Source attribution for responses when working with documents +- **[Web Search](./message-features.md#web-search)** - Real-time web information retrieval +- **[Code Execution](./message-features.md#code-execution)** - Python code execution in secure sandbox +- **[Computer Use](./message-features.md#computer-use)** - GUI interaction capabilities (screenshots, mouse, keyboard) +- **[Bash Commands](./message-features.md#bash-commands)** - Shell command execution +- **[Text Editor](./message-features.md#text-editor)** - Text file editing capabilities +- **[MCP Integration](./message-features.md#mcp-integration)** - Model Context Protocol tool integration +- **[Memory Tools](./message-features.md#memory-tools)** - Persistent memory across conversations + +**[→ Message Features Documentation](./message-features.md)** + +### Resource APIs + +Manage resources and process messages at scale: + +- **[Skills API](./skills.md)** - Create and manage reusable capabilities with version control +- **[Files API](./files.md)** - Upload and manage files for use in conversations +- **[Beta Message Batches](./batches.md)** - Process multiple beta messages asynchronously with all features + +## Quick Start + +### Enable Extended Thinking + +```python +from anthropic import Anthropic + +client = Anthropic() + +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=4096, + thinking={"type": "enabled", "budget_tokens": 2000}, + messages=[ + {"role": "user", "content": "Solve this complex problem: ..."} + ] +) + +# Access thinking and response +for block in message.content: + if block.type == "thinking": + print(f"Reasoning: {block.thinking}") + elif block.type == "text": + print(f"Answer: {block.text}") +``` + +### Combine Multiple Features + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=4096, + thinking={"type": "enabled"}, + web_search={"type": "enabled"}, + citations={"type": "enabled"}, + messages=[ + { + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": pdf_data + } + }, + {"type": "text", "text": "Analyze this paper and find related research"} + ] + } + ] +) +``` + +## Beta Messages API + +The beta messages API extends the standard messages API with additional parameters. + +```python { .api } +def create( + self, + *, + model: str, + messages: list[BetaMessageParam], + max_tokens: int, + # Standard parameters + system: str | list[BetaTextBlockParam] = NOT_GIVEN, + metadata: MetadataParam = NOT_GIVEN, + stop_sequences: list[str] = NOT_GIVEN, + stream: bool = False, + temperature: float = NOT_GIVEN, + top_p: float = NOT_GIVEN, + top_k: int = NOT_GIVEN, + tools: list[BetaToolParam] = NOT_GIVEN, + tool_choice: BetaToolChoice = NOT_GIVEN, + # Beta feature parameters + thinking: ThinkingConfigParam = NOT_GIVEN, + citations: CitationsConfigParam = NOT_GIVEN, + web_search: WebSearchConfigParam = NOT_GIVEN, + code_execution: CodeExecutionConfigParam = NOT_GIVEN, + bash: BashConfigParam = NOT_GIVEN, + text_editor: TextEditorConfigParam = NOT_GIVEN, + computer_use: ComputerUseConfigParam = NOT_GIVEN, + mcp: MCPConfigParam = NOT_GIVEN, + memory: MemoryConfigParam = NOT_GIVEN, + context: ContextConfigParam = NOT_GIVEN, + **kwargs +) -> BetaMessage: + """ + Create message with beta features. + + All standard message parameters are supported, plus beta feature configurations. + Multiple beta features can be enabled simultaneously. + + Returns: + BetaMessage with beta content blocks + """ + ... + +async def create(self, **kwargs) -> BetaMessage: + """Async version of create.""" + ... +``` + +### Streaming + +```python { .api } +def stream( + self, + **kwargs +) -> BetaMessageStreamManager: + """Stream message with beta features.""" + ... + +def stream(self, **kwargs) -> BetaAsyncMessageStreamManager: + """Async version of stream.""" + ... +``` + +### Count Tokens + +```python { .api } +def count_tokens( + self, + *, + model: str, + messages: list[BetaMessageParam], + system: str | list[BetaTextBlockParam] = NOT_GIVEN, + tools: list[BetaToolParam] = NOT_GIVEN, + tool_choice: BetaToolChoice = NOT_GIVEN, + **kwargs +) -> BetaMessageTokensCount: + """Count tokens for beta message.""" + ... +``` + +### Tool Runner + +```python { .api } +def tool_runner( + self, + *, + model: str, + messages: list[BetaMessageParam], + max_tokens: int, + tools: list[BetaToolParam | BetaFunctionTool], + **kwargs +) -> Iterator[BetaMessage]: + """Automatically execute tools in beta messages.""" + ... +``` + +## Beta Models API + +Retrieve model information with beta feature support. + +```python { .api } +def retrieve( + self, + model_id: str, + **kwargs +) -> BetaModelInfo: + """Get information about a specific model.""" + ... + +def list( + self, + *, + before_id: str = NOT_GIVEN, + after_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + **kwargs +) -> SyncPage[BetaModelInfo]: + """List available models with pagination.""" + ... +``` + +## Architecture + +Beta features are organized into three categories: + +### 1. Message Enhancement Features +Parameters that enhance `client.beta.messages.create()`: +- Enable advanced reasoning (thinking) +- Add real-time information (web search) +- Enable code execution +- Support document citations +- Enable computer interaction + +**[→ Full Documentation](./message-features.md)** + +### 2. Batch Processing +Process multiple beta messages with all features: +- Create batches with thinking, citations, web search, etc. +- Same interface as standard batches +- 50% cost reduction + +**[→ Batches Documentation](./batches.md)** + +### 3. Resource Management +Manage files and skills: +- Upload files for document analysis +- Create reusable skills +- Version control for skills + +**[→ Skills Documentation](./skills.md)** | **[→ Files Documentation](./files.md)** + +## Feature Comparison + +| Feature | Standard API | Beta API | +|---------|-------------|----------| +| Basic messaging | ✓ | ✓ | +| Streaming | ✓ | ✓ | +| Tool use | ✓ | ✓ | +| Extended thinking | ✗ | ✓ | +| Citations | ✗ | ✓ | +| Web search | ✗ | ✓ | +| Code execution | ✗ | ✓ | +| Computer use | ✗ | ✓ | +| Batches | ✓ | ✓ (with beta features) | +| Skills management | ✗ | ✓ | +| File management | ✗ | ✓ | + +## Important Notes + +### Stability and Changes +- **Beta features may change without notice** +- APIs may be modified or removed +- Breaking changes possible between releases +- Not recommended for production-critical features + +### Availability +- Not all features available in all regions +- Some features require specific model versions +- Check current documentation for availability + +### Usage and Costs +- Rate limits may differ for beta features +- Beta features may have additional usage costs +- Token budgets (e.g., thinking) are approximate +- Web search adds latency to requests + +### Technical Limitations +- Citations work best with document inputs (PDFs, text files) +- Code execution runs in isolated sandbox with limited packages +- Computer use requires display configuration and has security restrictions +- Web search requires internet connectivity +- Skills must include a SKILL.md file at the root +- File uploads have size limits (check API documentation) + +### Best Practices +- Test beta features in development before deploying +- Handle feature deprecation gracefully +- Monitor for API changes and updates +- Use appropriate error handling for experimental features +- Check feature availability before use + +## Migration from Standard API + +Most code using the standard API can be upgraded to beta with minimal changes: + +```python +# Standard API +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) + +# Beta API (backward compatible) +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) + +# Beta API (with features) +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + thinking={"type": "enabled"}, # Add beta features + messages=[{"role": "user", "content": "Hello"}] +) +``` + +## Documentation Structure + +### Message Enhancement Features +**[message-features.md](./message-features.md)** - Comprehensive guide to all message enhancement parameters: +- Extended thinking with budget control +- Citations configuration and usage +- Web search integration +- Code execution in sandbox +- Computer use capabilities +- Bash, text editor, MCP, memory tools +- Combined features examples +- Streaming with beta features + +### Resource APIs +**[batches.md](./batches.md)** - Beta message batches: +- Create batches with beta features +- Retrieve, list, cancel, delete operations +- Process results with JSONL decoder +- All beta features supported in batches + +**[skills.md](./skills.md)** - Skills management: +- Create and manage reusable skills +- Version control for skills +- List, retrieve, delete operations +- Skill file requirements + +**[files.md](./files.md)** - File management: +- Upload files for document analysis +- Download and list files +- Delete file operations +- File size limits and formats + +## See Also + +- [Messages API](../api/messages.md) - Core message creation +- [Tool Use API](../api/tools.md) - Tool integration +- [Streaming API](../api/streaming.md) - Streaming responses +- [Batches API](../api/batches.md) - Standard batch processing +- [Getting Started](../guides/getting-started.md) - Basic SDK usage diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md new file mode 100644 index 000000000..be7157a54 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md @@ -0,0 +1,1134 @@ +# Beta Message Enhancement Features + +Message enhancement features are parameters that add advanced capabilities to `client.beta.messages.create()`. Multiple features can be combined in a single request. + +## Extended Thinking + +Enable Claude to show detailed reasoning process with configurable token budget. + +### API + +```python { .api } +class ThinkingConfigParam(TypedDict): + """ + Extended thinking configuration. + + Fields: + type: "enabled" or "disabled" + budget_tokens: Maximum tokens for thinking (optional) + """ + type: Literal["enabled", "disabled"] + budget_tokens: NotRequired[int] + +class ThinkingBlock(BaseModel): + """ + Thinking content block in response. + + Attributes: + type: Always "thinking" + thinking: The reasoning content + """ + type: Literal["thinking"] + thinking: str + +class RedactedThinkingBlock(BaseModel): + """ + Redacted thinking block (when thinking disabled mid-conversation). + + Attributes: + type: Always "redacted_thinking" + """ + type: Literal["redacted_thinking"] +``` + +### Example + +```python +from anthropic import Anthropic + +client = Anthropic() + +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=4096, + thinking={ + "type": "enabled", + "budget_tokens": 2000, + }, + messages=[ + { + "role": "user", + "content": "Solve this complex math problem: ..." + } + ] +) + +# Check for thinking blocks +for block in message.content: + if block.type == "thinking": + print("Claude's reasoning:") + print(block.thinking) + elif block.type == "text": + print("\nFinal answer:") + print(block.text) +``` + +### Notes + +- Token budgets are approximate and may be exceeded slightly +- Thinking blocks appear before text blocks in the response +- Disabling thinking mid-conversation results in redacted blocks +- Works with all model versions that support beta features + +## Structured Outputs + +Enable type-safe, validated responses by specifying a Pydantic model schema. Claude will generate output that conforms to your schema and the SDK will automatically parse and validate it. + +### API + +```python { .api } +from typing import Generic, TypeVar, Optional +from pydantic import BaseModel + +ResponseFormatT = TypeVar('ResponseFormatT', bound=BaseModel) + +class ParsedBetaMessage(BetaMessage, Generic[ResponseFormatT]): + """ + Beta message response with parsed structured output. + + Attributes: + content: List of content blocks including ParsedBetaTextBlock + parsed_output: Convenience property to access the parsed Pydantic model + """ + content: list[ParsedBetaContentBlock[ResponseFormatT]] + + @property + def parsed_output(self) -> Optional[ResponseFormatT]: + """Extract the first parsed output from text content blocks.""" + ... + +class ParsedBetaTextBlock(BetaTextBlock, Generic[ResponseFormatT]): + """ + Text content block with parsed structured output. + + Attributes: + type: Always "text" + text: Raw JSON text + parsed_output: Validated Pydantic model instance + """ + type: Literal["text"] + text: str + parsed_output: Optional[ResponseFormatT] + +def transform_schema( + json_schema: type[BaseModel] | dict[str, Any], +) -> dict[str, Any]: + """ + Transform a Pydantic model or JSON schema for API compatibility. + + Handles: + - Format conversion for supported types + - Property transformations + - Unsupported property documentation + + Args: + json_schema: Pydantic BaseModel class or dict schema + + Returns: + Transformed schema dict compatible with API + """ + ... +``` + +### Basic Example + +```python +import pydantic +from anthropic import Anthropic + +# Define your output schema +class Order(pydantic.BaseModel): + product_name: str + price: float + quantity: int + +client = Anthropic() + +prompt = """ +Extract the product name, price, and quantity from this customer message: +"Hi, I'd like to order 2 packs of Green Tea for 5.50 dollars each." +""" + +# Use parse() method with output_format parameter +parsed_message = client.beta.messages.parse( + model="claude-sonnet-4-5-20250929", + messages=[{"role": "user", "content": prompt}], + max_tokens=1024, + output_format=Order, +) + +# Access parsed output directly +order = parsed_message.parsed_output +print(f"Product: {order.product_name}") +print(f"Price: ${order.price}") +print(f"Quantity: {order.quantity}") +# Output: +# Product: Green Tea +# Price: $5.5 +# Quantity: 2 +``` + +### Streaming Example + +```python +# Stream with structured outputs +with client.beta.messages.stream( + model="claude-sonnet-4-5-20250929", + messages=[{"role": "user", "content": prompt}], + max_tokens=1024, + output_format=Order, +) as stream: + for event in stream: + if event.type == "text": + # Get partial parsed output as it streams + partial = event.parsed_snapshot() + print(f"Partial: {partial}") + +# Get final parsed result +final_message = stream.get_final_message() +order = final_message.parsed_output +``` + +### Complex Schema Example + +```python +from typing import Literal +import pydantic + +class Address(pydantic.BaseModel): + street: str + city: str + postal_code: str + country: str + +class Customer(pydantic.BaseModel): + name: str + email: str + phone: str | None = None + address: Address + +class OrderItem(pydantic.BaseModel): + product_id: str + product_name: str + quantity: int + price: float + +class CompleteOrder(pydantic.BaseModel): + order_id: str + customer: Customer + items: list[OrderItem] + total_amount: float + status: Literal["pending", "confirmed", "shipped", "delivered"] + notes: str | None = None + +# Use with complex nested schema +parsed_message = client.beta.messages.parse( + model="claude-sonnet-4-5-20250929", + messages=[{ + "role": "user", + "content": "Extract order details from: [long email or document]" + }], + max_tokens=2048, + output_format=CompleteOrder, +) + +order = parsed_message.parsed_output +print(f"Order ID: {order.order_id}") +print(f"Customer: {order.customer.name}") +print(f"Items: {len(order.items)}") +for item in order.items: + print(f" - {item.product_name}: ${item.price} x {item.quantity}") +print(f"Total: ${order.total_amount}") +``` + +### Using transform_schema() + +```python +from anthropic import transform_schema + +# Transform a Pydantic model +class MyModel(pydantic.BaseModel): + name: str + age: int = pydantic.Field(ge=0, le=150, description="Age in years") + +# Get transformed schema +schema = transform_schema(MyModel) +print(schema) +# Transforms Pydantic schema to API-compatible format +# Unsupported constraints (ge, le) are moved to description + +# Or transform a dict schema directly +schema = transform_schema({ + "type": "integer", + "minimum": 1, + "maximum": 10, + "description": "A number" +}) +# Returns: {'type': 'integer', 'description': 'A number\n\n{minimum: 1, maximum: 10}'} +``` + +### Async Example + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + parsed_message = await client.beta.messages.parse( + model="claude-sonnet-4-5-20250929", + messages=[{"role": "user", "content": prompt}], + max_tokens=1024, + output_format=Order, + ) + + order = parsed_message.parsed_output + print(f"Extracted: {order}") + +asyncio.run(main()) +``` + +### Async Streaming Example + +```python +async def main(): + client = AsyncAnthropic() + + async with client.beta.messages.stream( + model="claude-sonnet-4-5-20250929", + messages=[{"role": "user", "content": prompt}], + max_tokens=1024, + output_format=Order, + ) as stream: + async for event in stream: + if event.type == "text": + partial = event.parsed_snapshot() + print(f"Partial: {partial}") + + final_message = await stream.get_final_message() + order = final_message.parsed_output + +asyncio.run(main()) +``` + +### Notes + +- Requires models that support structured outputs (e.g., claude-sonnet-4-5-20250929) +- Uses Pydantic for schema definition and validation +- The `parse()` method is a convenience wrapper around `create()` that handles schema transformation +- Automatically validates output against your schema +- Raises `pydantic.ValidationError` if output doesn't match schema +- In streaming mode, `parsed_snapshot()` provides incremental parsing +- The feature automatically adds `"structured-outputs-2025-11-13"` beta header +- Supported formats: date-time, time, date, duration, email, hostname, uri, ipv4, ipv6, uuid +- Unsupported Pydantic constraints (ge, le, gt, lt, etc.) are documented in schema description + +## Context Management + +Automatically manage conversation context by clearing thinking blocks or tool uses when limits are approached. + +### API + +```python { .api } +class BetaContextManagementConfigParam(TypedDict, total=False): + edits: list[Union[BetaClearToolUses20250919EditParam, BetaClearThinking20251015EditParam]] + +class BetaClearThinking20251015EditParam(TypedDict, total=False): + type: Literal["clear_thinking_20251015"] + keep: Union[int, Literal["all"]] # Number of recent turns to keep thinking blocks + +class BetaClearToolUses20250919EditParam(TypedDict, total=False): + type: Literal["clear_tool_uses_20250919"] + trigger: Union[BetaInputTokensTriggerParam, BetaToolUsesTriggerParam] + keep: BetaToolUsesKeepParam + clear_at_least: Optional[BetaInputTokensClearAtLeastParam] + clear_tool_inputs: Union[bool, list[str], None] + exclude_tools: Optional[list[str]] +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + thinking={"type": "enabled"}, + context_management={ + "edits": [ + {"type": "clear_thinking_20251015", "keep": 3}, # Keep last 3 turns + { + "type": "clear_tool_uses_20250919", + "trigger": {"type": "input_tokens", "value": 100000}, + "keep": {"type": "turns", "value": 5}, + "clear_tool_inputs": True, + } + ] + }, + messages=conversation_history, +) +``` + +## Container Support + +Enable container-based file operations for beta features. + +### API + +```python { .api } +class BetaContainerParams(TypedDict, total=False): + # Container configuration parameters (check API docs for details) + pass + +class BetaContainerUploadBlock(BaseModel): + type: Literal["container_upload"] + # Upload result information +``` + +## Search Tools + +Enable document search capabilities with BM25 or regex patterns. + +### API + +```python { .api } +class BetaToolSearchToolBM2520251119Param(TypedDict, total=False): + """BM25 search tool for document retrieval.""" + type: Literal["search_tool_bm25_20251119"] + name: str + +class BetaToolSearchToolRegex20251119Param(TypedDict, total=False): + """Regex pattern search tool.""" + type: Literal["search_tool_regex_20251119"] + name: str +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[ + {"type": "search_tool_bm25_20251119", "name": "document_search"}, + ], + messages=[{"role": "user", "content": "Search for relevant passages about AI"}] +) +``` + +## Citations + +Enable source attribution for responses when working with documents. + +### API + +```python { .api } +class CitationsConfigParam(TypedDict): + """ + Citations configuration. + + Fields: + type: "enabled" or "disabled" + """ + type: Literal["enabled", "disabled"] + +class TextCitation(BaseModel): + """ + Citation in text content. + + Attributes: + type: Always "text_citation" + text: Cited text + cited_text: Original source text + location: Location in source + """ + type: Literal["text_citation"] + text: str + cited_text: str + location: CitationCharLocation | CitationContentBlockLocation | CitationPageLocation +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + citations={"type": "enabled"}, + messages=[ + { + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": pdf_data, + }, + }, + { + "type": "text", + "text": "Summarize this document with citations.", + }, + ], + } + ] +) + +# Extract citations +for block in message.content: + if hasattr(block, 'citations'): + for citation in block.citations: + print(f"Citation: {citation.cited_text}") + print(f"Location: {citation.location}") +``` + +### Notes + +- Works best with document inputs (PDFs, text files) +- Citations reference specific locations in source documents +- Multiple citations can appear in a single response +- Location types: character position, content block, or page number + +## Web Search + +Enable real-time web information retrieval. + +### API + +```python { .api } +class WebSearchConfigParam(TypedDict): + """ + Web search configuration. + + Fields: + type: "enabled" or "disabled" + """ + type: Literal["enabled", "disabled"] + +class WebSearchResultBlock(BaseModel): + """ + Web search result in response. + + Attributes: + type: Always "web_search_result" + url: Result URL + title: Page title + snippet: Text snippet + """ + type: Literal["web_search_result"] + url: str + title: str + snippet: str +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + web_search={"type": "enabled"}, + messages=[ + { + "role": "user", + "content": "What are the latest AI developments in 2025?" + } + ] +) + +# Check for web search results +for block in message.content: + if block.type == "web_search_result": + print(f"Found: {block.title}") + print(f"URL: {block.url}") + print(f"Snippet: {block.snippet}") +``` + +### Notes + +- Requires internet connectivity +- Adds latency to requests (typically 1-3 seconds) +- Results are current at time of request +- May not be available in all regions + +## Code Execution + +Enable Python code execution in secure sandbox environment. + +### API + +```python { .api } +class CodeExecutionConfigParam(TypedDict): + """ + Code execution configuration. + + Fields: + type: "enabled" or "disabled" + """ + type: Literal["enabled", "disabled"] +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + code_execution={"type": "enabled"}, + messages=[ + { + "role": "user", + "content": "Calculate the first 10 Fibonacci numbers." + } + ] +) +``` + +### Notes + +- Runs in isolated sandbox with limited packages +- Security restrictions prevent file system access +- Network access is disabled +- Execution timeout applies (typically 30 seconds) +- Available packages: NumPy, Pandas, Matplotlib, etc. (check docs) + +## Computer Use + +Enable computer interaction capabilities including screenshots, mouse, and keyboard control. + +### API + +```python { .api } +class ComputerUseConfigParam(TypedDict): + """ + Computer use configuration. + + Fields: + type: "enabled" or "disabled" + display_width_px: Display width in pixels + display_height_px: Display height in pixels + display_number: Display number (optional) + """ + type: Literal["enabled", "disabled"] + display_width_px: NotRequired[int] + display_height_px: NotRequired[int] + display_number: NotRequired[int] +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + computer_use={ + "type": "enabled", + "display_width_px": 1920, + "display_height_px": 1080, + }, + messages=[ + { + "role": "user", + "content": "Open a web browser and search for Python tutorials." + } + ] +) +``` + +### Notes + +- Requires display configuration +- Security restrictions apply +- Not available in all regions +- Use with caution - can interact with GUI +- Suitable for automation and testing scenarios + +## Bash Commands + +Enable bash command execution. + +### API + +```python { .api } +class BashConfigParam(TypedDict): + """ + Bash configuration. + + Fields: + type: "enabled" or "disabled" + """ + type: Literal["enabled", "disabled"] + +class ToolBash20250124(TypedDict): + """ + Bash tool definition. + + Fields: + type: Always "bash_20250124" + name: Tool name + """ + type: Literal["bash_20250124"] + name: str +``` + +### Notes + +- Executes shell commands in sandboxed environment +- Security restrictions apply +- Limited access to system resources +- Use with caution in production + +## Text Editor + +Enable text file editing capabilities. + +### API + +```python { .api } +class TextEditorConfigParam(TypedDict): + """ + Text editor configuration. + + Fields: + type: "enabled" or "disabled" + """ + type: Literal["enabled", "disabled"] + +class ToolTextEditor20250124(TypedDict): + """ + Text editor tool (latest version). + + Fields: + type: Always "text_editor_20250124" + name: Tool name + """ + type: Literal["text_editor_20250124"] + name: str +``` + +### Notes + +- Supports text file creation and editing +- Works in sandboxed environment +- File operations are temporary unless persisted +- Suitable for code generation and modification + +## MCP Integration + +Enable Model Context Protocol tool integration for custom external tools. + +### API + +```python { .api } +class BetaRequestMCPServerURLDefinitionParam(TypedDict, total=False): + """MCP server URL definition.""" + url: Required[str] + """URL of the MCP server""" + +class BetaMCPToolsetParam(TypedDict, total=False): + """MCP toolset configuration.""" + servers: list[BetaRequestMCPServerURLDefinitionParam] + """List of MCP server definitions""" + +class BetaMCPToolConfigParam(TypedDict, total=False): + """MCP tool configuration.""" + # Tool-specific configuration + +class BetaMCPToolUseBlock(BaseModel): + """MCP tool use in response.""" + type: Literal["mcp_tool_use"] + # MCP tool execution information + +class BetaMCPToolResultBlock(BaseModel): + """MCP tool result in response.""" + type: Literal["mcp_tool_result"] + # MCP tool result data +``` + +### Example + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + mcp_servers=[ + {"url": "http://localhost:8080/mcp"}, + ], + messages=[ + {"role": "user", "content": "Use the MCP tools to fetch data"} + ] +) + +# Check for MCP tool uses +for block in message.content: + if block.type == "mcp_tool_use": + print(f"MCP tool used: {block}") + elif block.type == "mcp_tool_result": + print(f"MCP result: {block}") +``` + +### Notes + +- Integrates external tools via Model Context Protocol +- Requires running MCP server(s) +- Enables custom tool integration beyond built-in tools +- Supports multiple MCP servers simultaneously +- Advanced feature for specialized use cases + +## Memory Tools + +Enable persistent memory across conversations. + +### API + +```python { .api } +class MemoryConfigParam(TypedDict): + """ + Memory configuration. + + Fields: + type: "enabled" or "disabled" + """ + type: Literal["enabled", "disabled"] + +class BetaAbstractMemoryTool: + """ + Abstract base class for memory tools. + + Subclass to implement custom memory backends. + """ + def store(self, key: str, value: Any) -> None: + """Store value in memory.""" + ... + + def retrieve(self, key: str) -> Any: + """Retrieve value from memory.""" + ... + + def delete(self, key: str) -> None: + """Delete value from memory.""" + ... +``` + +### Example - Custom Memory Implementation + +```python +# Example custom memory tool implementation +class DatabaseMemoryTool(BetaAbstractMemoryTool): + def __init__(self, db_connection): + self.db = db_connection + + def store(self, key: str, value: Any) -> None: + """Store value in database.""" + self.db.execute("INSERT INTO memory (key, value) VALUES (?, ?)", (key, value)) + + def retrieve(self, key: str) -> Any: + """Retrieve value from database.""" + result = self.db.execute("SELECT value FROM memory WHERE key = ?", (key,)).fetchone() + return result[0] if result else None + + def delete(self, key: str) -> None: + """Delete value from database.""" + self.db.execute("DELETE FROM memory WHERE key = ?", (key,)) + +# Use with beta messages +db_memory = DatabaseMemoryTool(db_conn) + +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + memory={"type": "enabled"}, + messages=[ + {"role": "user", "content": "Remember that my favorite color is blue."} + ] +) +``` + +### Notes + +- Enables persistent context across conversations +- Requires custom memory backend implementation +- Useful for multi-turn conversations with state +- Memory persists beyond single request + +## Combined Features Example + +Use multiple beta features together for maximum capability: + +```python +# Use multiple beta features in one request +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=4096, + thinking={"type": "enabled", "budget_tokens": 2000}, + web_search={"type": "enabled"}, + code_execution={"type": "enabled"}, + citations={"type": "enabled"}, + messages=[ + { + "role": "user", + "content": [ + { + "type": "document", + "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data} + }, + { + "type": "text", + "text": "Analyze this research paper, search for recent related work, and write Python code to replicate key results. Include citations." + } + ] + } + ] +) + +# Process response with all beta features +for block in message.content: + if block.type == "thinking": + print(f"Reasoning: {block.thinking}") + elif block.type == "text": + print(f"Analysis: {block.text}") + if hasattr(block, 'citations'): + print("Citations:") + for citation in block.citations: + print(f" - {citation.cited_text}") + elif block.type == "web_search_result": + print(f"Related work: {block.title} ({block.url})") +``` + +## Streaming with Beta Features + +Stream responses while using beta features: + +```python +with client.beta.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + thinking={"type": "enabled"}, + messages=[ + {"role": "user", "content": "Explain quantum computing"} + ] +) as stream: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "thinking_delta": + print(f"[Thinking: {event.delta.thinking}]") + elif event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + print() + +# Get final message +message = stream.get_final_message() +print(f"\nTokens used: {message.usage.output_tokens}") +``` + +## Async Beta Messages + +Use beta features with async client: + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + message = await client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + thinking={"type": "enabled"}, + messages=[ + {"role": "user", "content": "Explain machine learning"} + ] + ) + + for block in message.content: + if block.type == "thinking": + print(f"Thinking: {block.thinking}") + elif block.type == "text": + print(f"Answer: {block.text}") + +asyncio.run(main()) +``` + +## Async Streaming with Beta Features + +Combine async streaming with beta features: + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + async with client.beta.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + thinking={"type": "enabled"}, + web_search={"type": "enabled"}, + messages=[ + {"role": "user", "content": "What are the latest quantum computing breakthroughs?"} + ] + ) as stream: + async for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "thinking_delta": + print(f"[Thinking: {event.delta.thinking}]") + elif event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + print() + +asyncio.run(main()) +``` + +## Tool Runner with Beta Tools + +Use tool runner with beta features: + +```python +from anthropic import beta_tool + +@beta_tool +def search_database(query: str) -> list: + """Search database for query.""" + return [{"id": 1, "title": "Result"}] + +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + tools=[search_database], + messages=[{"role": "user", "content": "Search for Python tutorials"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +## Best Practices + +### 1. Choose Appropriate Features + +Only enable features you need: +```python +# Don't do this (unnecessary features) +message = client.beta.messages.create( + thinking={"type": "enabled"}, + web_search={"type": "enabled"}, + code_execution={"type": "enabled"}, + messages=[{"role": "user", "content": "What is 2+2?"}] # Simple question +) + +# Do this (appropriate features) +message = client.beta.messages.create( + messages=[{"role": "user", "content": "What is 2+2?"}] # No features needed +) +``` + +### 2. Set Reasonable Token Budgets + +For thinking, set budgets appropriate to task complexity: +```python +# Simple task +thinking={"type": "enabled", "budget_tokens": 500} + +# Complex reasoning +thinking={"type": "enabled", "budget_tokens": 2000} +``` + +### 3. Handle Feature-Specific Content + +Check for feature-specific content blocks: +```python +for block in message.content: + if block.type == "thinking": + # Handle reasoning + ... + elif block.type == "web_search_result": + # Handle search result + ... + elif block.type == "text": + # Handle text + ... +``` + +### 4. Test Features Individually + +Test each feature separately before combining: +```python +# Test thinking alone +message1 = client.beta.messages.create( + thinking={"type": "enabled"}, + messages=[...] +) + +# Test web search alone +message2 = client.beta.messages.create( + web_search={"type": "enabled"}, + messages=[...] +) + +# Combine after testing +message3 = client.beta.messages.create( + thinking={"type": "enabled"}, + web_search={"type": "enabled"}, + messages=[...] +) +``` + +### 5. Monitor Costs + +Beta features may have additional costs: +```python +# Track usage +print(f"Input tokens: {message.usage.input_tokens}") +print(f"Output tokens: {message.usage.output_tokens}") +if message.usage.cache_creation_input_tokens: + print(f"Cache creation: {message.usage.cache_creation_input_tokens}") +``` + +## Limitations and Considerations + +### Token Usage +- Extended thinking increases token usage +- Web search adds overhead +- Budget accordingly for production use + +### Latency +- Web search adds 1-3 seconds +- Code execution adds execution time +- Computer use may have delays + +### Availability +- Not all features available in all regions +- Some features require specific models +- Check documentation for current availability + +### Security +- Code execution is sandboxed +- Computer use has security restrictions +- Bash commands are limited +- Use appropriate caution in production + +### Stability +- Beta features may change +- Breaking changes possible +- Monitor for deprecations +- Test after SDK updates + +## See Also + +- [Beta Overview](./index.md) - Overview of all beta features +- [Beta Batches](./batches.md) - Use beta features in batches +- [Messages API](../api/messages.md) - Core message creation +- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md new file mode 100644 index 000000000..a2dcc2df8 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md @@ -0,0 +1,599 @@ +# Beta Skills API + +Create and manage reusable capabilities with version control. + +## Overview + +The Skills API allows you to create, manage, and version reusable capabilities that can be used across conversations. Skills are defined by uploading files including a SKILL.md file that describes the skill's functionality. + +## Key Features + +- Create skills with file uploads +- Version control for skill evolution +- List and filter skills by source (custom or Anthropic-provided) +- Delete skills and specific versions +- Cursor-based pagination for scalability + +## Skills API + +### Create Skill + +```python { .api } +def create( + self, + *, + display_title: str | None = NOT_GIVEN, + files: list[FileTypes] | None = NOT_GIVEN, + **kwargs +) -> SkillCreateResponse: + """ + Create a skill. + + Parameters: + display_title: Display title for the skill (human-readable label, not included in model prompt) + files: Files to upload for the skill + All files must be in the same top-level directory + Must include a SKILL.md file at the root + + Returns: + SkillCreateResponse object + """ + ... +``` + +### Retrieve Skill + +```python { .api } +def retrieve( + self, + skill_id: str, + **kwargs +) -> SkillRetrieveResponse: + """ + Retrieve skill by ID. + + Parameters: + skill_id: Unique identifier for the skill + + Returns: + SkillRetrieveResponse with skill details + """ + ... +``` + +### List Skills + +```python { .api } +def list( + self, + *, + limit: int = NOT_GIVEN, + page: str | None = NOT_GIVEN, + source: str | None = NOT_GIVEN, + **kwargs +) -> SyncPageCursor[SkillListResponse]: + """ + List skills with cursor-based pagination. + + Parameters: + limit: Number of results per page (max 100, default 20) + page: Pagination token from previous response's next_page field + source: Filter by source ("custom" for user-created, "anthropic" for Anthropic-created) + + Returns: + SyncPageCursor[SkillListResponse] with paginated results + """ + ... +``` + +### Delete Skill + +```python { .api } +def delete( + self, + skill_id: str, + **kwargs +) -> SkillDeleteResponse: + """ + Delete a skill. + + Parameters: + skill_id: Unique identifier for the skill + + Returns: + SkillDeleteResponse confirming deletion + """ + ... +``` + +## Skill Versions API + +Manage versions of existing skills for evolution and rollback capabilities. + +### Create Version + +```python { .api } +def create( + self, + skill_id: str, + *, + files: list[FileTypes] | None = NOT_GIVEN, + **kwargs +) -> VersionCreateResponse: + """ + Create a new version of a skill. + + Parameters: + skill_id: Unique identifier for the skill + files: Files to upload for the skill version + Must include a SKILL.md file at the root + + Returns: + VersionCreateResponse with new version details + """ + ... +``` + +### Retrieve Version + +```python { .api } +def retrieve( + self, + version: str, + *, + skill_id: str, + **kwargs +) -> VersionRetrieveResponse: + """ + Get details about a specific skill version. + + Parameters: + version: Version identifier + skill_id: Unique identifier for the skill + + Returns: + VersionRetrieveResponse with version details + """ + ... +``` + +### List Versions + +```python { .api } +def list( + self, + skill_id: str, + *, + limit: int | None = NOT_GIVEN, + page: str | None = NOT_GIVEN, + **kwargs +) -> SyncPageCursor[VersionListResponse]: + """ + List versions of a skill. + + Parameters: + skill_id: Unique identifier for the skill + limit: Number of items to return per page (default 20, range 1-1000) + page: Pagination token from previous response's next_page field + + Returns: + SyncPageCursor[VersionListResponse] with cursor-paginated versions + """ + ... +``` + +### Delete Version + +```python { .api } +def delete( + self, + version: str, + *, + skill_id: str, + **kwargs +) -> VersionDeleteResponse: + """ + Delete a specific skill version. + + Parameters: + version: Version identifier + skill_id: Unique identifier for the skill + + Returns: + VersionDeleteResponse confirming deletion + """ + ... +``` + +## Examples + +### Create Simple Skill + +```python +from anthropic import Anthropic +from anthropic._utils import file_from_path + +client = Anthropic() + +# Create skill with SKILL.md +skill = client.beta.skills.create( + display_title="Weather Analyzer", + files=[ + file_from_path("SKILL.md"), # Required: skill description + file_from_path("weather.py"), # Optional: implementation files + ] +) + +print(f"Created skill: {skill.id}") +print(f"Display title: {skill.display_title}") +``` + +### SKILL.md Format + +```markdown +# Weather Analyzer + +Analyze weather data and provide recommendations. + +## Capabilities + +- Temperature analysis +- Humidity assessment +- Weather recommendations + +## Usage + +Call this skill with temperature and humidity values to get weather analysis. +``` + +### List Skills + +```python +# List all custom skills +for skill in client.beta.skills.list(source="custom"): + print(f"Skill: {skill.display_title} ({skill.id})") + +# List Anthropic-provided skills +for skill in client.beta.skills.list(source="anthropic"): + print(f"Anthropic skill: {skill.display_title}") + +# Paginated listing +page_result = client.beta.skills.list(limit=10) +for skill in page_result: + print(f"Skill: {skill.display_title}") + +# Get next page +if page_result.next_page: + next_page = client.beta.skills.list(limit=10, page=page_result.next_page) +``` + +### Retrieve Skill + +```python +skill = client.beta.skills.retrieve("skill_abc123") +print(f"Name: {skill.display_title}") +print(f"Created: {skill.created_at}") +print(f"Latest version: {skill.latest_version}") +``` + +### Delete Skill + +```python +# Delete entire skill +deleted = client.beta.skills.delete("skill_abc123") +print(f"Deleted skill: {deleted.id}") +``` + +### Create New Version + +```python +# Create new version with updated files +version = client.beta.skills.versions.create( + skill_id="skill_abc123", + files=[ + file_from_path("SKILL.md"), # Updated description + file_from_path("weather_v2.py"), # New implementation + ] +) + +print(f"Created version: {version.version}") +print(f"Version ID: {version.id}") +``` + +### List Versions + +```python +# List all versions of a skill +for version in client.beta.skills.versions.list(skill_id="skill_abc123"): + print(f"Version: {version.version}") + print(f"Created: {version.created_at}") + print(f"Status: {version.status}") + print("---") + +# Paginated version listing +versions = client.beta.skills.versions.list( + skill_id="skill_abc123", + limit=5 +) + +for version in versions: + print(f"Version: {version.version}") + +# Get next page +if versions.next_page: + next_versions = client.beta.skills.versions.list( + skill_id="skill_abc123", + limit=5, + page=versions.next_page + ) +``` + +### Retrieve Specific Version + +```python +version = client.beta.skills.versions.retrieve( + skill_id="skill_abc123", + version="v1" +) + +print(f"Version: {version.version}") +print(f"Created: {version.created_at}") +``` + +### Delete Version + +```python +# Delete specific version +deleted = client.beta.skills.versions.delete( + skill_id="skill_abc123", + version="v1" +) + +print(f"Deleted version: {deleted.version}") +``` + +### Version Management Pattern + +```python +# Create skill +skill = client.beta.skills.create( + display_title="Data Processor", + files=[file_from_path("SKILL.md"), file_from_path("processor_v1.py")] +) + +print(f"Created skill {skill.id} with version {skill.latest_version}") + +# Update skill with new version +v2 = client.beta.skills.versions.create( + skill_id=skill.id, + files=[file_from_path("SKILL.md"), file_from_path("processor_v2.py")] +) + +print(f"Created version {v2.version}") + +# List all versions +versions = list(client.beta.skills.versions.list(skill_id=skill.id)) +print(f"Total versions: {len(versions)}") + +# Rollback by deleting latest version (if needed) +if len(versions) > 1: + latest = versions[0] + client.beta.skills.versions.delete( + skill_id=skill.id, + version=latest.version + ) + print(f"Rolled back to previous version") +``` + +### Multi-File Skill + +```python +# Create skill with multiple files +skill = client.beta.skills.create( + display_title="Advanced Calculator", + files=[ + file_from_path("SKILL.md"), + file_from_path("calculator.py"), + file_from_path("math_utils.py"), + file_from_path("constants.py"), + ] +) + +print(f"Created skill with {len(skill.files)} files") +``` + +### Async Skills Operations + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + # Create skill asynchronously + skill = await client.beta.skills.create( + display_title="Async Processor", + files=[file_from_path("SKILL.md")] + ) + + # List skills + async for skill in client.beta.skills.list(): + print(f"Skill: {skill.display_title}") + + # Create version + version = await client.beta.skills.versions.create( + skill_id=skill.id, + files=[file_from_path("SKILL.md")] + ) + + print(f"Created version: {version.version}") + +asyncio.run(main()) +``` + +### Error Handling + +```python +from anthropic import APIError, BadRequestError + +try: + # Create skill + skill = client.beta.skills.create( + display_title="Test Skill", + files=[file_from_path("SKILL.md")] + ) +except BadRequestError as e: + if "SKILL.md" in str(e): + print("Error: SKILL.md file is required at root") + else: + print(f"Invalid request: {e.message}") +except APIError as e: + print(f"API error: {e.message}") + +# Validate skill exists before operations +try: + skill = client.beta.skills.retrieve("skill_abc123") + print(f"Skill exists: {skill.display_title}") +except APIError: + print("Skill not found") +``` + +## File Requirements + +### SKILL.md File + +- **Required**: Every skill must include a SKILL.md file at the root +- **Purpose**: Describes the skill's capabilities and usage +- **Format**: Markdown file with skill documentation +- **Location**: Must be in the root directory with other skill files + +### File Organization + +- All skill files must be in the same top-level directory +- No subdirectories allowed +- Include all necessary implementation files +- Keep files focused and modular + +### Example Structure + +``` +skill_files/ + ├── SKILL.md # Required: skill description + ├── main.py # Optional: implementation + ├── utils.py # Optional: utilities + └── constants.json # Optional: configuration +``` + +## Best Practices + +### 1. Clear SKILL.md Documentation + +Write comprehensive SKILL.md files: +```markdown +# Skill Name + +Clear one-line description. + +## Capabilities + +- Bullet list of what the skill does +- Specific use cases + +## Usage + +How to invoke and use the skill. + +## Requirements + +Any prerequisites or constraints. +``` + +### 2. Version Management + +- Create new versions for updates instead of deleting and recreating +- Keep version history for rollback capability +- Document changes in SKILL.md for each version + +### 3. Naming Conventions + +Use descriptive display titles: +```python +# Good +display_title="Weather Data Analyzer" + +# Bad +display_title="skill1" +``` + +### 4. File Organization + +Keep related functionality together: +```python +files=[ + file_from_path("SKILL.md"), + file_from_path("analyzer.py"), # Main logic + file_from_path("data_utils.py"), # Helper functions + file_from_path("config.json"), # Configuration +] +``` + +### 5. Error Handling + +Always validate operations: +```python +try: + skill = client.beta.skills.retrieve(skill_id) + # Use skill +except APIError: + # Handle missing skill + pass +``` + +### 6. Pagination + +Use cursor pagination for large lists: +```python +page = None +while True: + results = client.beta.skills.list(limit=100, page=page) + for skill in results: + process_skill(skill) + + if not results.next_page: + break + page = results.next_page +``` + +## Limitations and Considerations + +### File Constraints + +- All files must be in same directory (no subdirectories) +- SKILL.md required at root +- File size limits apply (check API documentation) + +### Version Limits + +- Check API for maximum versions per skill +- Old versions remain until explicitly deleted +- Deleting a skill deletes all versions + +### Source Filtering + +- "custom": User-created skills +- "anthropic": Anthropic-provided skills +- Use source filter to separate your skills from system skills + +### Pagination + +- Default page size: 20 +- Maximum page size: 100 (skills), 1000 (versions) +- Use next_page token for cursor-based pagination + +## See Also + +- [Beta Overview](./index.md) - Overview of all beta features +- [Files API](./files.md) - File upload and management +- [Beta Batches](./batches.md) - Batch processing with beta features +- [Message Features](./message-features.md) - Beta message enhancement features diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md new file mode 100644 index 000000000..fc961b722 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md @@ -0,0 +1,336 @@ +# Basic Messaging Tasks + +Practical patterns for common messaging scenarios. For complete API reference, see **[Messages API](../api/messages.md)**. + +## Send Simple Text Message + +```python +from anthropic import Anthropic + +client = Anthropic() # Uses ANTHROPIC_API_KEY env var + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[ + {"role": "user", "content": "Explain quantum computing in simple terms"} + ] +) + +print(message.content[0].text) +``` + +## Use System Prompt + +System prompts guide Claude's behavior and style: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + system="You are a helpful Python programming assistant. Be concise and provide code examples.", + messages=[ + {"role": "user", "content": "How do I read a CSV file?"} + ] +) +``` + +### System Prompt with Caching + +Cache long system prompts to reduce costs: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + system=[ + { + "type": "text", + "text": "You are an expert on Shakespeare's works. Here is context:\n\n" + long_context, + "cache_control": {"type": "ephemeral"} + } + ], + messages=[ + {"role": "user", "content": "Analyze Hamlet's soliloquy"} + ] +) + +# Check cache usage +print(f"Cache hits: {message.usage.cache_read_input_tokens}") +print(f"Cache misses: {message.usage.cache_creation_input_tokens}") +``` + +## Multi-Turn Conversation + +Maintain conversation history: + +```python +conversation = [] + +# Turn 1 +conversation.append({"role": "user", "content": "My name is Alice"}) + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=conversation +) + +conversation.append({ + "role": "assistant", + "content": message.content +}) + +# Turn 2 +conversation.append({"role": "user", "content": "What's my name?"}) + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=conversation +) + +print(message.content[0].text) # "Your name is Alice" +``` + +### Conversation Manager Helper + +```python +class ConversationManager: + def __init__(self, client: Anthropic, model: str, system: str = None): + self.client = client + self.model = model + self.system = system + self.history = [] + + def send(self, user_message: str) -> str: + """Send message and update history""" + self.history.append({"role": "user", "content": user_message}) + + message = self.client.messages.create( + model=self.model, + max_tokens=1024, + system=self.system, + messages=self.history + ) + + assistant_message = message.content[0].text + self.history.append({ + "role": "assistant", + "content": message.content + }) + + return assistant_message + +# Usage +conv = ConversationManager( + client, + model="claude-sonnet-4-5-20250929", + system="You are a helpful assistant" +) + +response1 = conv.send("Hello, I'm Bob") +response2 = conv.send("What's my name?") # Remembers "Bob" +``` + +## Control Response Temperature + +Temperature affects randomness (0.0 = deterministic, 1.0 = creative): + +```python +# Deterministic/factual responses +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + temperature=0.0, + messages=[{"role": "user", "content": "What is 2+2?"}] +) + +# Creative/varied responses +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + temperature=1.0, + messages=[{"role": "user", "content": "Write a creative story"}] +) +``` + +## Use Stop Sequences + +Stop generation at specific strings: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + stop_sequences=["", "\n\n---"], + messages=[ + { + "role": "user", + "content": "List 5 colors, each on a new line. End with " + } + ] +) + +# Check if stop sequence was hit +if message.stop_reason == "stop_sequence": + print(f"Stopped at: {message.stop_sequence}") +``` + +## Track Token Usage + +Monitor input and output tokens for cost management: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) + +usage = message.usage +print(f"Input tokens: {usage.input_tokens}") +print(f"Output tokens: {usage.output_tokens}") +print(f"Total: {usage.input_tokens + usage.output_tokens}") + +# With caching +if usage.cache_read_input_tokens: + print(f"Cache hits: {usage.cache_read_input_tokens} tokens") +if usage.cache_creation_input_tokens: + print(f"New cache entries: {usage.cache_creation_input_tokens} tokens") +``` + +## Count Tokens Before Sending + +Estimate costs without creating a message: + +```python +token_count = client.messages.count_tokens( + model="claude-sonnet-4-5-20250929", + messages=[ + {"role": "user", "content": "Very long message..."} + ] +) + +print(f"This request will use {token_count.input_tokens} input tokens") + +# Check if within budget +MAX_TOKENS = 100000 +if token_count.input_tokens > MAX_TOKENS: + print("Message too long, truncating...") +``` + +## Add Request Metadata + +Track users for rate limiting and abuse prevention: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + metadata={"user_id": "user_12345"}, + messages=[{"role": "user", "content": "Hello"}] +) +``` + +## Handle Max Tokens Limit + +Deal with incomplete responses: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=100, # Intentionally small + messages=[{"role": "user", "content": "Write a long essay"}] +) + +if message.stop_reason == "max_tokens": + print("Response was truncated. Consider increasing max_tokens.") + print(f"Partial response: {message.content[0].text}") + + # Continue generation + continued = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[ + {"role": "user", "content": "Write a long essay"}, + {"role": "assistant", "content": message.content}, + {"role": "user", "content": "Please continue"} + ] + ) +``` + +## Use Different Models + +Choose model based on requirements: + +```python +# Fast and cost-effective +message = client.messages.create( + model="claude-3-5-haiku-20241022", + max_tokens=1024, + messages=[{"role": "user", "content": "Quick question"}] +) + +# Balanced (recommended for most tasks) +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Standard query"}] +) + +# Maximum capability for complex tasks +message = client.messages.create( + model="claude-opus-4-5-20250929", + max_tokens=2048, + messages=[{"role": "user", "content": "Complex reasoning task"}] +) +``` + +## Async Messaging + +For async applications: + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def send_message(content: str) -> str: + client = AsyncAnthropic() + + message = await client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": content}] + ) + + return message.content[0].text + +# Run async function +response = asyncio.run(send_message("Hello")) + +# Concurrent requests +async def send_multiple(): + client = AsyncAnthropic() + + tasks = [ + client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": f"Question {i}"}] + ) + for i in range(5) + ] + + responses = await asyncio.gather(*tasks) + return [r.content[0].text for r in responses] + +results = asyncio.run(send_multiple()) +``` + +## See Also + +- **[Messages API Reference](../api/messages.md)** - Complete API documentation +- **[Multimodal Input](./multimodal-input.md)** - Working with images and documents +- **[Streaming Responses](./streaming-responses.md)** - Real-time streaming +- **[Error Handling Guide](../guides/error-handling.md)** - Production error patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md new file mode 100644 index 000000000..16b426b64 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md @@ -0,0 +1,421 @@ +# Multimodal Input Tasks + +Practical patterns for working with images, PDFs, and documents. For complete reference, see **[Multimodal Guide](../guides/multimodal.md)**. + +## Analyze Image from File + +```python +import base64 +from anthropic import Anthropic + +client = Anthropic() + +# Read and encode image +with open("image.jpg", "rb") as f: + image_data = base64.standard_b64encode(f.read()).decode() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": image_data + } + }, + {"type": "text", "text": "What's in this image?"} + ] + }] +) + +print(message.content[0].text) +``` + +## Supported Image Formats + +```python +# JPEG +with open("photo.jpg", "rb") as f: + data = base64.standard_b64encode(f.read()).decode() + source = {"type": "base64", "media_type": "image/jpeg", "data": data} + +# PNG +with open("screenshot.png", "rb") as f: + data = base64.standard_b64encode(f.read()).decode() + source = {"type": "base64", "media_type": "image/png", "data": data} + +# GIF +with open("animation.gif", "rb") as f: + data = base64.standard_b64encode(f.read()).decode() + source = {"type": "base64", "media_type": "image/gif", "data": data} + +# WebP +with open("image.webp", "rb") as f: + data = base64.standard_b64encode(f.read()).decode() + source = {"type": "base64", "media_type": "image/webp", "data": data} +``` + +## Analyze Multiple Images + +Compare or analyze multiple images together: + +```python +import base64 + +def load_image(path: str) -> str: + with open(path, "rb") as f: + return base64.standard_b64encode(f.read()).decode() + +img1 = load_image("before.jpg") +img2 = load_image("after.jpg") + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "text", "text": "Before:"}, + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img1}}, + {"type": "text", "text": "After:"}, + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img2}}, + {"type": "text", "text": "What changed between these images?"} + ] + }] +) +``` + +## Optimize Large Images + +Reduce token usage by resizing images: + +```python +from PIL import Image +import io +import base64 + +def optimize_image(image_path: str, max_size=(1024, 1024)) -> str: + """Resize and optimize image for API""" + img = Image.open(image_path) + + # Resize maintaining aspect ratio + img.thumbnail(max_size, Image.Resampling.LANCZOS) + + # Convert to JPEG with compression + buffer = io.BytesIO() + img = img.convert("RGB") # Ensure RGB mode + img.save(buffer, format="JPEG", quality=85, optimize=True) + + return base64.standard_b64encode(buffer.getvalue()).decode() + +# Use optimized image +optimized_data = optimize_image("large_photo.jpg") + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": optimized_data}}, + {"type": "text", "text": "Analyze this image"} + ] + }] +) +``` + +## Process PDF Document + +```python +import base64 + +with open("document.pdf", "rb") as f: + pdf_data = base64.standard_b64encode(f.read()).decode() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": pdf_data + } + }, + {"type": "text", "text": "Summarize the key points in this document"} + ] + }] +) +``` + +## PDF from URL + +Process publicly accessible PDFs without downloading: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "url", + "media_type": "application/pdf", + "url": "https://example.com/document.pdf" + } + }, + {"type": "text", "text": "Extract the main findings"} + ] + }] +) +``` + +## Process Plain Text Document + +For large text content: + +```python +long_text = """Very long text content...""" + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "text", + "media_type": "text/plain", + "data": long_text + } + }, + {"type": "text", "text": "Analyze the sentiment of this text"} + ] + }] +) +``` + +## Mix Text, Images, and Documents + +Combine multiple content types: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + {"type": "text", "text": "Here's a presentation:"}, + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": pdf_data + } + }, + {"type": "text", "text": "And the cover image:"}, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": image_data + } + }, + {"type": "text", "text": "Review both and provide feedback"} + ] + }] +) +``` + +## Common Image Analysis Tasks + +### Extract Text (OCR) + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_data}}, + {"type": "text", "text": "Extract all text from this image"} + ] + }] +) +``` + +### Identify Objects + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_data}}, + {"type": "text", "text": "List all objects visible in this image"} + ] + }] +) +``` + +### Answer Questions About Image + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_data}}, + {"type": "text", "text": "Is there a person in this image? If so, describe them."} + ] + }] +) +``` + +## Common Document Tasks + +### Summarize Document + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, + {"type": "text", "text": "Provide a 3-paragraph summary"} + ] + }] +) +``` + +### Q&A on Document + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, + {"type": "text", "text": "What methodology was used in this study?"} + ] + }] +) +``` + +### Extract Specific Information + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, + {"type": "text", "text": "Extract all dates, names, and monetary amounts mentioned"} + ] + }] +) +``` + +## Use Citations (Beta) + +Get source attribution for document-based responses: + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + citations={"type": "enabled"}, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, + {"type": "text", "text": "Summarize with citations"} + ] + }] +) + +# Check citations +for block in message.content: + if hasattr(block, 'citations'): + for citation in block.citations: + print(f"Cited: {citation.cited_text}") +``` + +## Helper Functions + +### Load Multiple Images + +```python +import base64 +from pathlib import Path + +def load_images(directory: str) -> list[dict]: + """Load all images from directory""" + content = [] + + for path in Path(directory).glob("*.{jpg,jpeg,png}"): + with open(path, "rb") as f: + data = base64.standard_b64encode(f.read()).decode() + + # Determine media type from extension + media_type = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png" + }[path.suffix.lower()] + + content.append({ + "type": "image", + "source": { + "type": "base64", + "media_type": media_type, + "data": data + } + }) + + return content + +# Use it +images = load_images("./photos") +images.append({"type": "text", "text": "Describe all these images"}) + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{"role": "user", "content": images}] +) +``` + +## See Also + +- **[Multimodal Guide](../guides/multimodal.md)** - Complete multimodal documentation +- **[Messages API Reference](../api/messages.md)** - API details +- **[Beta Citations](../beta/message-features.md#citations)** - Source attribution feature diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md new file mode 100644 index 000000000..f0d3c7d32 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md @@ -0,0 +1,450 @@ +# Streaming Responses Tasks + +Practical patterns for real-time streaming. For complete reference, see **[Streaming API](../api/streaming.md)** and **[Streaming Guide](../guides/streaming-guide.md)**. + +## Basic Text Streaming + +```python +from anthropic import Anthropic + +client = Anthropic() + +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a short story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +print() # New line after stream ends +``` + +**That's it!** The `.text_stream` property automatically filters out non-text events and gives you text deltas ready to print. + +## Get Final Message After Streaming + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "What is 2+2?"}] +) as stream: + # Stream the text + for text in stream.text_stream: + print(text, end="", flush=True) + +# Get complete message with metadata +message = stream.get_final_message() +print(f"\n\nToken usage: {message.usage.output_tokens}") +print(f"Stop reason: {message.stop_reason}") +``` + +## Process All Events + +For more control, iterate over all stream events: + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) as stream: + for event in stream: + if event.type == "message_start": + print(f"[Stream started: {event.message.id}]") + + elif event.type == "content_block_start": + print(f"\n[Content block {event.index} started]") + + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + + elif event.type == "content_block_stop": + print(f"\n[Content block {event.index} stopped]") + + elif event.type == "message_delta": + print(f"\n[Stop reason: {event.delta.stop_reason}]") + print(f"[Tokens used: {event.usage.output_tokens}]") + + elif event.type == "message_stop": + print("\n[Stream completed]") +``` + +## Stream with Tool Use + +Detect when Claude wants to use tools: + +```python +tools = [{ + "name": "get_weather", + "description": "Get weather", + "input_schema": { + "type": "object", + "properties": { + "location": {"type": "string"} + }, + "required": ["location"] + } +}] + +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=tools, + messages=[{"role": "user", "content": "What's the weather in SF?"}] +) as stream: + for event in stream: + if event.type == "content_block_start": + if event.content_block.type == "tool_use": + print(f"\n[Tool call: {event.content_block.name}]") + + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + elif event.delta.type == "input_json_delta": + print(event.delta.partial_json, end="") + +message = stream.get_final_message() + +# Process tool calls +for block in message.content: + if block.type == "tool_use": + print(f"\nTool: {block.name}") + print(f"Input: {block.input}") +``` + +## Track Token Usage During Streaming + +Monitor token usage in real-time: + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a long essay"}] +) as stream: + current_tokens = 0 + + for event in stream: + if event.type == "message_delta": + current_tokens = event.usage.output_tokens + print(f"\r[Tokens: {current_tokens}]", end="") + + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) +``` + +## Async Streaming + +For async applications: + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def stream_response(): + client = AsyncAnthropic() + + async with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a haiku"}] + ) as stream: + async for text in stream.text_stream: + print(text, end="", flush=True) + print() + +asyncio.run(stream_response()) +``` + +## Concurrent Async Streams + +Run multiple streams in parallel: + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def stream_question(client: AsyncAnthropic, question: str) -> str: + """Stream a question and return final text""" + async with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": question}] + ) as stream: + # Consume stream + async for _ in stream: + pass + return stream.get_final_text() + +async def main(): + client = AsyncAnthropic() + + questions = [ + "What is Python?", + "What is JavaScript?", + "What is Rust?" + ] + + # Run all streams concurrently + results = await asyncio.gather(*[ + stream_question(client, q) for q in questions + ]) + + for question, answer in zip(questions, results): + print(f"\nQ: {question}") + print(f"A: {answer}") + +asyncio.run(main()) +``` + +## Buffered Streaming + +Buffer output for smoother display: + +```python +import time + +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a story"}] +) as stream: + buffer = "" + + for text in stream.text_stream: + buffer += text + + # Flush every 10 characters or at punctuation + if len(buffer) >= 10 or text in ".!?\n": + print(buffer, end="", flush=True) + buffer = "" + time.sleep(0.02) # Smooth animation + + # Flush remaining + if buffer: + print(buffer, end="", flush=True) +``` + +## Current Message Snapshot + +Get partial message during streaming: + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Count to 10"}] +) as stream: + for event in stream: + # Access current accumulated message + snapshot = stream.current_message_snapshot + + if snapshot.content: + current_text = snapshot.content[0].text if snapshot.content[0].type == "text" else "" + print(f"\rCurrent length: {len(current_text)}", end="") +``` + +## Error Handling in Streams + +```python +from anthropic import APIError, APITimeoutError + +try: + with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +except APITimeoutError: + print("\n[Stream timed out]") +except APIError as e: + print(f"\n[Stream error: {e.message}]") +``` + +## Multi-Turn Conversation with Streaming + +```python +conversation = [] + +def stream_turn(user_message: str): + """Stream a conversation turn""" + conversation.append({"role": "user", "content": user_message}) + + print(f"\nUser: {user_message}") + print("Claude: ", end="") + + with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=conversation + ) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + + message = stream.get_final_message() + conversation.append({ + "role": "assistant", + "content": message.content + }) + print() # New line + +# Conversation +stream_turn("Hi, I'm Alice") +stream_turn("What's my name?") # Claude remembers "Alice" +``` + +## Stream with System Prompt + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + system="You are a helpful Python expert. Be concise.", + messages=[{"role": "user", "content": "Explain list comprehensions"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +## Stream with Temperature + +```python +# Creative streaming +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + temperature=0.8, + messages=[{"role": "user", "content": "Write a creative story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +## Stream Beta Features + +Stream with extended thinking (beta): + +```python +with client.beta.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + thinking={"type": "enabled", "budget_tokens": 1000}, + messages=[{"role": "user", "content": "Solve this complex problem: ..."}] +) as stream: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "thinking_delta": + print(f"[Thinking: {event.delta.thinking}]", end="") + elif event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) +``` + +## Access Raw HTTP Response + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) as stream: + # Access underlying HTTP response + request_id = stream.response.headers.get("request-id") + print(f"[Request ID: {request_id}]") + + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +## Interrupt Streaming + +Handle keyboard interrupts gracefully: + +```python +import signal +import sys + +def signal_handler(sig, frame): + print("\n[Streaming interrupted]") + sys.exit(0) + +signal.signal(signal.SIGINT, signal_handler) + +try: + with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a very long story"}] + ) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +except KeyboardInterrupt: + print("\n[Streaming stopped]") +``` + +## Manual Stream Iteration + +For advanced use cases without context manager: + +```python +stream_manager = client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) + +stream = stream_manager.__enter__() + +try: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + + message = stream.get_final_message() +finally: + stream_manager.__exit__(None, None, None) +``` + +## Streaming Best Practices + +### 1. Always Use Context Managers + +```python +# Good - automatic cleanup +with client.messages.stream(...) as stream: + for text in stream.text_stream: + print(text, end="") + +# Bad - manual cleanup required +stream = client.messages.stream(...) +# ... easy to forget cleanup +``` + +### 2. Set Appropriate Timeouts + +```python +import httpx + +client = Anthropic( + timeout=httpx.Timeout(120.0) # 2 minutes for long streams +) +``` + +### 3. Handle Interruptions + +Always handle potential interruptions gracefully for better UX. + +### 4. Use Async for High Concurrency + +When handling many concurrent streams, use `AsyncAnthropic` for better performance. + +## See Also + +- **[Streaming API Reference](../api/streaming.md)** - Complete event types and architecture +- **[Streaming Guide](../guides/streaming-guide.md)** - Advanced patterns +- **[Messages API](../api/messages.md)** - Core message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md new file mode 100644 index 000000000..08637d1ed --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md @@ -0,0 +1,484 @@ +# Tool Integration Tasks + +Practical patterns for function calling and agentic workflows. For complete reference, see **[Tools API](../api/tools.md)** and **[Tool Usage Guide](../guides/tool-usage.md)**. + +## Quick Start with Tool Decorator + +```python +from anthropic import Anthropic, beta_tool + +client = Anthropic() + +# Define tool with decorator +@beta_tool +def get_weather(location: str, unit: str = "fahrenheit") -> dict: + """ + Get weather for a location. + + Args: + location: City and state, e.g. "San Francisco, CA" + unit: Temperature unit - "celsius" or "fahrenheit" + """ + # Your implementation here + return { + "location": location, + "temperature": 72, + "unit": unit, + "condition": "sunny" + } + +# Auto-execute tools with tool_runner +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather], + messages=[{"role": "user", "content": "What's the weather in NYC?"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +**That's it!** The `tool_runner` automatically: +- Sends the request to Claude +- Detects when Claude wants to use a tool +- Executes the tool function +- Sends results back to Claude +- Continues until Claude responds with text + +## Manual Tool Handling + +For more control over the execution flow: + +```python +# 1. Define tool manually +tools = [{ + "name": "get_weather", + "description": "Get weather for a location", + "input_schema": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "City and state"} + }, + "required": ["location"] + } +}] + +# 2. Send initial request +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=tools, + messages=[{"role": "user", "content": "What's the weather in SF?"}] +) + +# 3. Check if Claude wants to use a tool +if message.stop_reason == "tool_use": + # Extract tool use + tool_use = next(block for block in message.content if block.type == "tool_use") + print(f"Claude wants to use: {tool_use.name}") + print(f"With params: {tool_use.input}") + + # 4. Execute the tool + result = get_weather(location=tool_use.input["location"]) + + # 5. Send result back to Claude + final_message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=tools, + messages=[ + {"role": "user", "content": "What's the weather in SF?"}, + {"role": "assistant", "content": message.content}, + { + "role": "user", + "content": [{ + "type": "tool_result", + "tool_use_id": tool_use.id, + "content": str(result) + }] + } + ] + ) + + print(final_message.content[0].text) +``` + +## Multiple Tools + +Claude can choose from multiple tools: + +```python +@beta_tool +def get_weather(location: str) -> dict: + """Get current weather for a location""" + return {"temp": 72, "condition": "sunny"} + +@beta_tool +def search_database(query: str, limit: int = 10) -> list: + """Search database for items matching query""" + return [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}] + +@beta_tool +def send_email(to: str, subject: str, body: str) -> dict: + """Send an email""" + return {"status": "sent", "message_id": "abc123"} + +# Claude picks the right tool(s) +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather, search_database, send_email], + messages=[{"role": "user", "content": "Check the weather in NYC and email it to bob@example.com"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +## Async Tools + +For I/O-bound operations: + +```python +import httpx +from anthropic import AsyncAnthropic, beta_async_tool + +client = AsyncAnthropic() + +@beta_async_tool +async def fetch_url(url: str) -> str: + """Fetch content from a URL""" + async with httpx.AsyncClient() as http_client: + response = await http_client.get(url) + return response.text + +# Use with async tool_runner +async for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[fetch_url], + messages=[{"role": "user", "content": "Fetch https://example.com and summarize it"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +## Control Tool Selection + +### Force Tool Use + +Require Claude to use at least one tool: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "any"}, # Force any tool + messages=[{"role": "user", "content": "Hello"}] +) +``` + +### Force Specific Tool + +Require Claude to use a specific tool: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "tool", "name": "get_weather"}, # Force specific tool + messages=[{"role": "user", "content": "Hello"}] +) +``` + +### Disable Tools for a Turn + +Temporarily disable tool use: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "none"}, # No tools this turn + messages=[...] +) +``` + +### Disable Parallel Tool Use + +Force sequential tool calls: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={ + "type": "auto", + "disable_parallel_tool_use": True + }, + messages=[{"role": "user", "content": "Do task A and then task B"}] +) +``` + +## Error Handling in Tools + +Report tool execution failures to Claude: + +```python +@beta_tool +def divide(a: float, b: float) -> float: + """Divide two numbers""" + if b == 0: + raise ValueError("Cannot divide by zero") + return a / b + +# Manual handling with error reporting +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[divide.to_param()], + messages=[{"role": "user", "content": "What is 10 divided by 0?"}] +) + +tool_use = next(block for block in message.content if block.type == "tool_use") + +# Execute and catch errors +try: + result = divide(**tool_use.input) + tool_result = str(result) + is_error = False +except Exception as e: + tool_result = str(e) + is_error = True + +# Send error back to Claude +final_message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[divide.to_param()], + messages=[ + {"role": "user", "content": "What is 10 divided by 0?"}, + {"role": "assistant", "content": message.content}, + { + "role": "user", + "content": [{ + "type": "tool_result", + "tool_use_id": tool_use.id, + "content": tool_result, + "is_error": is_error # Tell Claude this is an error + }] + } + ] +) + +print(final_message.content[0].text) # Claude handles the error gracefully +``` + +## Stateful Tools + +Tools that maintain state: + +```python +class Calculator: + def __init__(self): + self.memory = 0 + + @beta_tool + def calculate(self, expression: str) -> float: + """Evaluate a mathematical expression and store result""" + result = eval(expression) # Use safe evaluation in production + self.memory = result + return result + + @beta_tool + def recall(self) -> float: + """Recall the last calculation result""" + return self.memory + + @beta_tool + def clear(self) -> str: + """Clear calculator memory""" + self.memory = 0 + return "Memory cleared" + +# Use stateful tools +calc = Calculator() + +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[calc.calculate, calc.recall, calc.clear], + messages=[{"role": "user", "content": "Calculate 5 * 8, then add 10 to that result"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) + print(f"Final memory: {calc.memory}") +``` + +## Validate Tool Inputs + +```python +@beta_tool +def set_temperature(degrees: float, unit: str = "celsius") -> dict: + """Set thermostat temperature""" + # Validate unit + if unit not in ["celsius", "fahrenheit"]: + raise ValueError(f"Invalid unit: {unit}. Must be 'celsius' or 'fahrenheit'") + + # Validate range + if degrees < -50 or degrees > 50: + raise ValueError(f"Temperature {degrees} out of safe range (-50 to 50)") + + # Set temperature + return {"status": "success", "temperature": degrees, "unit": unit} +``` + +## Return Structured Data + +```python +from typing import TypedDict + +class UserInfo(TypedDict): + id: str + name: str + email: str + created_at: str + +@beta_tool +def get_user_info(user_id: str) -> UserInfo: + """Get user information by ID""" + # Fetch from database + return { + "id": user_id, + "name": "Alice Smith", + "email": "alice@example.com", + "created_at": "2024-01-15" + } + +# Claude can work with structured data +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_user_info], + messages=[{"role": "user", "content": "Get info for user user_123 and send them a welcome email"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +## Complex Tool Example + +Database query tool with validation: + +```python +@beta_tool +def query_database( + table: str, + filters: dict, + limit: int = 10, + sort_by: str | None = None +) -> list[dict]: + """ + Query database with filters. + + Args: + table: Table name to query + filters: Key-value pairs for filtering (e.g., {"status": "active"}) + limit: Maximum number of results (1-100) + sort_by: Optional field to sort by + """ + # Validate inputs + valid_tables = ["users", "orders", "products"] + if table not in valid_tables: + raise ValueError(f"Invalid table. Must be one of: {valid_tables}") + + if not 1 <= limit <= 100: + raise ValueError("Limit must be between 1 and 100") + + # Execute query (pseudocode) + results = db.query(table).filter(**filters).limit(limit) + if sort_by: + results = results.sort(sort_by) + + return results.all() +``` + +## Tool Usage Best Practices + +### 1. Clear Descriptions + +Write clear docstrings - Claude uses these to decide when to call the tool: + +```python +@beta_tool +def search_products( + query: str, + category: str | None = None, + min_price: float | None = None, + max_price: float | None = None, + in_stock_only: bool = True +) -> list[dict]: + """ + Search product catalog for matching items. + + Use this tool when the user wants to find or browse products. + + Args: + query: Search keywords or product name + category: Optional category filter (e.g., "electronics", "books", "clothing") + min_price: Minimum price in USD + max_price: Maximum price in USD + in_stock_only: Only show products currently in stock + """ + ... +``` + +### 2. Use Type Hints + +Type hints improve schema generation: + +```python +from typing import Literal + +@beta_tool +def book_appointment( + date: str, # Use specific format hints in docstring + time: str, + service: Literal["haircut", "coloring", "styling"] +) -> dict: + """ + Book an appointment. + + Args: + date: Date in YYYY-MM-DD format + time: Time in HH:MM format (24-hour) + service: Type of service + """ + ... +``` + +### 3. Return Useful Data + +Return structured data that Claude can work with: + +```python +@beta_tool +def get_order_status(order_id: str) -> dict: + """Get status of an order""" + return { + "order_id": order_id, + "status": "shipped", + "tracking_number": "1Z999AA10123456784", + "estimated_delivery": "2024-01-20", + "items_count": 3 + } +``` + +## See Also + +- **[Tools API Reference](../api/tools.md)** - Complete API documentation +- **[Tool Usage Guide](../guides/tool-usage.md)** - Advanced patterns and examples +- **[Messages API](../api/messages.md)** - Core message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md new file mode 100644 index 000000000..32996caad --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md @@ -0,0 +1,288 @@ +# Batch Processing Guide + +Process thousands of messages asynchronously with 50% cost reduction using the Message Batches API. + +## Why Use Batches? + +- **Cost Reduction**: 50% lower cost compared to standard API +- **High Throughput**: Process thousands of requests asynchronously +- **No Rate Limits**: Batch requests don't count against rate limits + +## Basic Batch + +```python +batch = client.messages.batches.create( + requests=[ + { + "custom_id": "request-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "What is AI?"}] + } + }, + { + "custom_id": "request-2", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "What is ML?"}] + } + } + ] +) + +print(f"Batch ID: {batch.id}") +``` + +## Check Status + +```python +batch = client.messages.batches.retrieve("batch_abc123") + +print(f"Status: {batch.processing_status}") +print(f"Succeeded: {batch.request_counts.succeeded}/{batch.request_counts.processing + batch.request_counts.succeeded}") +print(f"Errored: {batch.request_counts.errored}") +``` + +## Poll Until Complete + +```python +import time + +batch_id = "batch_abc123" + +while True: + batch = client.messages.batches.retrieve(batch_id) + + if batch.processing_status == "ended": + break + + print(f"Processing: {batch.request_counts.processing} requests remaining") + time.sleep(60) # Check every minute + +print("Batch complete!") +``` + +## Get Results + +```python +results = client.messages.batches.results(batch_id) + +for response in results: + if response.result.type == "succeeded": + print(f"{response.custom_id}: {response.result.message.content[0].text}") + elif response.result.type == "errored": + print(f"{response.custom_id}: Error - {response.result.error.message}") +``` + +## Process Results + +```python +results_by_id = {} + +for response in client.messages.batches.results(batch_id): + if response.result.type == "succeeded": + results_by_id[response.custom_id] = response.result.message.content[0].text + elif response.result.type == "errored": + results_by_id[response.custom_id] = f"Error: {response.result.error.message}" + +# Access results by custom_id +print(results_by_id["request-1"]) +``` + +## Large-Scale Batch + +```python +# Generate batch requests +requests = [] +for i, question in enumerate(questions): # thousands of questions + requests.append({ + "custom_id": f"question-{i}", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": question}] + } + }) + +# Create batch +batch = client.messages.batches.create(requests=requests) + +# Poll for completion +while True: + batch = client.messages.batches.retrieve(batch.id) + if batch.processing_status == "ended": + break + time.sleep(60) + +# Process results +for response in client.messages.batches.results(batch.id): + save_result(response.custom_id, response.result) +``` + +## Cancel Batch + +```python +batch = client.messages.batches.cancel("batch_abc123") +print(f"Status: {batch.processing_status}") # "canceling" +``` + +## Delete Batch + +```python +# Must be ended first +deleted = client.messages.batches.delete("batch_abc123") +print(f"Deleted: {deleted.id}") +``` + +## List Batches + +```python +# List all batches +for batch in client.messages.batches.list(limit=10): + print(f"{batch.id}: {batch.processing_status}") + +# Paginate manually +page = client.messages.batches.list(limit=10) +for batch in page.data: + print(batch.id) + +if page.has_next_page(): + next_page = page.get_next_page() +``` + +## Batch with Tools + +```python +batch = client.messages.batches.create( + requests=[ + { + "custom_id": "weather-sf", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "tools": [{ + "name": "get_weather", + "description": "Get weather", + "input_schema": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"] + } + }], + "messages": [{"role": "user", "content": "What's the weather in SF?"}] + } + } + ] +) +``` + +## Batch with Streaming Context + +While batches don't stream, you can prepare stream-like prompts: + +```python +requests = [] +for doc in documents: + requests.append({ + "custom_id": f"doc-{doc['id']}", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 2048, + "messages": [{ + "role": "user", + "content": [ + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": doc['data']}}, + {"type": "text", "text": "Summarize this document"} + ] + }] + } + }) +``` + +## Async Batch Operations + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + # Create batch + batch = await client.messages.batches.create(requests=[...]) + + # Poll until complete + while True: + batch = await client.messages.batches.retrieve(batch.id) + if batch.processing_status == "ended": + break + await asyncio.sleep(60) + + # Get results + results = [] + async for response in client.messages.batches.results(batch.id): + results.append(response) + + return results + +results = asyncio.run(main()) +``` + +## Best Practices + +### 1. Use Meaningful Custom IDs + +```python +requests = [{ + "custom_id": f"user-{user_id}-question-{question_id}", + "params": {...} +}] +``` + +### 2. Handle Errors Gracefully + +```python +for response in client.messages.batches.results(batch_id): + if response.result.type == "succeeded": + process_success(response) + elif response.result.type == "errored": + log_error(response.custom_id, response.result.error) + retry_if_needed(response.custom_id) +``` + +### 3. Batch Size Considerations + +- Batches can contain up to 10,000 requests +- Consider splitting very large workloads into multiple batches + +### 4. Monitor Progress + +```python +def monitor_batch(batch_id): + while True: + batch = client.messages.batches.retrieve(batch_id) + + total = sum([ + batch.request_counts.processing, + batch.request_counts.succeeded, + batch.request_counts.errored + ]) + + completed = batch.request_counts.succeeded + batch.request_counts.errored + progress = (completed / total * 100) if total > 0 else 0 + + print(f"Progress: {progress:.1f}% ({completed}/{total})") + + if batch.processing_status == "ended": + break + + time.sleep(60) +``` + +## See Also + +- [Batches API](../api/batches.md) - Complete API reference +- [Messages API](../api/messages.md) - Message creation +- [Beta Features](../beta/index.md) - Beta message batches diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md new file mode 100644 index 000000000..03f65c0b5 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md @@ -0,0 +1,324 @@ +# Error Handling Guide + +Robust error handling patterns for production applications using the Anthropic Python SDK. + +## Basic Error Handling + +```python +from anthropic import APIError + +try: + message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) +except APIError as e: + print(f"Error: {e.message}") +``` + +## Specific Error Types + +```python +from anthropic import ( + RateLimitError, + AuthenticationError, + BadRequestError, + NotFoundError, + InternalServerError, +) + +try: + message = client.messages.create(...) +except RateLimitError as e: + retry_after = e.response.headers.get("retry-after") + print(f"Rate limited. Retry after {retry_after}s") +except AuthenticationError: + print("Invalid API key") +except BadRequestError as e: + print(f"Invalid request: {e.message}") +except NotFoundError: + print("Resource not found") +except InternalServerError: + print("Server error, please retry") +``` + +## Retry with Exponential Backoff + +```python +import time +from anthropic import RateLimitError, InternalServerError + +def create_message_with_retry(max_retries=3, base_delay=1.0): + """Create message with exponential backoff retry.""" + for attempt in range(max_retries): + try: + return client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) + except (RateLimitError, InternalServerError) as e: + if attempt == max_retries - 1: + raise + + wait_time = base_delay * (2 ** attempt) + print(f"Retry {attempt + 1}/{max_retries} after {wait_time}s") + time.sleep(wait_time) + +message = create_message_with_retry() +``` + +## Advanced Retry Pattern + +```python +import random +import time +from anthropic import APIError, RateLimitError + +def exponential_backoff_retry( + func, + max_retries=5, + base_delay=1.0, + max_delay=60.0, + jitter=True +): + """ + Execute function with exponential backoff retry. + + Args: + func: Function to execute + max_retries: Maximum retry attempts + base_delay: Initial delay in seconds + max_delay: Maximum delay in seconds + jitter: Add random jitter to avoid thundering herd + """ + for attempt in range(max_retries): + try: + return func() + except RateLimitError as e: + if attempt == max_retries - 1: + raise + + # Use retry-after header if available + retry_after = e.response.headers.get("retry-after") + if retry_after: + wait_time = float(retry_after) + else: + wait_time = min(base_delay * (2 ** attempt), max_delay) + if jitter: + wait_time *= (0.5 + random.random()) + + print(f"Rate limited. Waiting {wait_time:.1f}s (attempt {attempt + 1}/{max_retries})") + time.sleep(wait_time) + except APIError as e: + if attempt == max_retries - 1: + raise + print(f"API error: {e.message}. Retrying...") + time.sleep(base_delay) + +# Usage +message = exponential_backoff_retry( + lambda: client.messages.create(...) +) +``` + +## Async Error Handling + +```python +import asyncio +from anthropic import AsyncAnthropic, APIError + +async def create_message_safe(): + client = AsyncAnthropic() + + try: + message = await client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) + return message + except APIError as e: + print(f"Error: {e.message}") + return None + +result = asyncio.run(create_message_safe()) +``` + +## Circuit Breaker Pattern + +```python +import time +from anthropic import APIError + +class CircuitBreaker: + """Circuit breaker to prevent cascading failures.""" + + def __init__(self, failure_threshold=5, timeout=60): + self.failure_count = 0 + self.failure_threshold = failure_threshold + self.timeout = timeout + self.last_failure_time = None + self.is_open = False + + def call(self, func, *args, **kwargs): + """Execute function with circuit breaker protection.""" + if self.is_open: + # Check if timeout has passed + if time.time() - self.last_failure_time < self.timeout: + raise Exception("Circuit breaker is open") + else: + # Try to close circuit + self.is_open = False + self.failure_count = 0 + + try: + result = func(*args, **kwargs) + self.failure_count = 0 # Reset on success + return result + except APIError as e: + self.failure_count += 1 + self.last_failure_time = time.time() + + if self.failure_count >= self.failure_threshold: + self.is_open = True + print(f"Circuit breaker opened after {self.failure_count} failures") + + raise + +# Usage +circuit_breaker = CircuitBreaker() + +try: + message = circuit_breaker.call( + client.messages.create, + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) +except Exception as e: + print(f"Circuit breaker error: {e}") +``` + +## Logging Errors + +```python +import logging +from anthropic import APIError, APIStatusError + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +try: + message = client.messages.create(...) +except APIStatusError as e: + logger.error( + "API request failed", + extra={ + "request_id": e.request_id, + "status_code": e.status_code, + "error_message": e.message, + "error_type": e.body.get("error", {}).get("type") if e.body else None, + } + ) +except APIError as e: + logger.error(f"API error: {e.message}") +``` + +## Graceful Degradation + +```python +from anthropic import APIError + +def get_response_with_fallback(user_message): + """Try primary model, fall back to simpler model on error.""" + models = [ + "claude-sonnet-4-5-20250929", + "claude-3-5-haiku-20241022", + ] + + for model in models: + try: + message = client.messages.create( + model=model, + max_tokens=1024, + messages=[{"role": "user", "content": user_message}] + ) + return message.content[0].text + except APIError as e: + print(f"Failed with {model}: {e.message}") + continue + + return "Service temporarily unavailable" + +response = get_response_with_fallback("What is AI?") +``` + +## Validate Inputs Before API Call + +```python +from anthropic import BadRequestError + +def validate_and_create_message(messages): + """Validate inputs before making API call.""" + # Validate message structure + if not messages: + raise ValueError("Messages list cannot be empty") + + for msg in messages: + if "role" not in msg or "content" not in msg: + raise ValueError("Each message must have 'role' and 'content'") + + if msg["role"] not in ["user", "assistant"]: + raise ValueError(f"Invalid role: {msg['role']}") + + # Make API call + try: + return client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=messages + ) + except BadRequestError as e: + print(f"API validation failed: {e.message}") + raise +``` + +## Best Practices + +### 1. Always Handle Exceptions + +Never let exceptions go unhandled in production. + +### 2. Use Specific Exception Types + +Catch specific exceptions for targeted handling. + +### 3. Implement Retry Logic + +Always retry transient errors (rate limits, server errors). + +### 4. Log with Context + +Include request IDs and relevant context in logs. + +### 5. Set Reasonable Timeouts + +```python +import httpx + +client = Anthropic( + timeout=httpx.Timeout(60.0), + max_retries=3 +) +``` + +### 6. Monitor Error Rates + +Track error rates to detect issues early. + +## See Also + +- [Error Reference](../reference/errors.md) - Complete exception hierarchy +- [Client Configuration](../reference/client-config.md) - Timeout and retry configuration diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md new file mode 100644 index 000000000..76cc0e2e0 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md @@ -0,0 +1,229 @@ +# Getting Started Guide + +Step-by-step guide to using the Anthropic Python SDK. + +## Installation + +```bash +pip install anthropic +``` + +Optional extras: +```bash +pip install anthropic[bedrock] # AWS Bedrock +pip install anthropic[vertex] # Google Vertex AI +pip install anthropic[aiohttp] # Alternative async HTTP +``` + +## Authentication + +Set your API key as an environment variable: + +```bash +export ANTHROPIC_API_KEY='your-api-key' +``` + +Or pass it explicitly: + +```python +from anthropic import Anthropic + +client = Anthropic(api_key="your-api-key") +``` + +## Basic Message + +```python +from anthropic import Anthropic + +client = Anthropic() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[ + {"role": "user", "content": "Hello, Claude!"} + ] +) + +print(message.content[0].text) +``` + +## System Prompts + +Configure Claude's behavior with system prompts: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + system="You are a helpful AI assistant specializing in Python programming.", + messages=[ + {"role": "user", "content": "How do I read a file?"} + ] +) +``` + +## Multi-Turn Conversations + +Maintain conversation history: + +```python +messages = [ + {"role": "user", "content": "My name is Alice."}, + {"role": "assistant", "content": "Hello Alice! Nice to meet you."}, + {"role": "user", "content": "What's my name?"} +] + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=messages +) + +print(message.content[0].text) # "Your name is Alice." +``` + +## Streaming Responses + +Stream responses for real-time feedback: + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[ + {"role": "user", "content": "Write a short story"} + ] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +print() +``` + +## Working with Images + +Send images to Claude: + +```python +import base64 + +with open("image.jpg", "rb") as f: + image_data = base64.standard_b64encode(f.read()).decode() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": image_data + } + }, + {"type": "text", "text": "What's in this image?"} + ] + }] +) +``` + +## Error Handling + +Always handle potential errors: + +```python +from anthropic import APIError, RateLimitError + +try: + message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) +except RateLimitError as e: + print(f"Rate limited. Retry after {e.response.headers.get('retry-after')}s") +except APIError as e: + print(f"API error: {e.message}") +``` + +## Async Usage + +For async applications: + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + message = await client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) + + print(message.content[0].text) + +asyncio.run(main()) +``` + +## Best Practices + +### 1. Use Context Managers + +```python +with Anthropic() as client: + message = client.messages.create(...) +# Client automatically closed +``` + +### 2. Handle Errors Gracefully + +```python +try: + message = client.messages.create(...) +except APIError as e: + # Handle error + ... +``` + +### 3. Use Appropriate Models + +- `claude-sonnet-4-5-20250929` - Balanced intelligence and speed +- `claude-opus-4-5-20250929` - Maximum capability +- `claude-3-5-haiku-20241022` - Fast and cost-effective + +### 4. Set Reasonable Timeouts + +```python +import httpx + +client = Anthropic( + timeout=httpx.Timeout(60.0) +) +``` + +### 5. Track Token Usage + +```python +message = client.messages.create(...) +print(f"Input tokens: {message.usage.input_tokens}") +print(f"Output tokens: {message.usage.output_tokens}") +``` + +## Next Steps + +- [Multimodal Content](./multimodal.md) - Images, documents, PDFs +- [Tool Usage](./tool-usage.md) - Function calling +- [Streaming Guide](./streaming-guide.md) - Advanced streaming +- [Error Handling](./error-handling.md) - Robust error management + +## See Also + +- [Messages API](../api/messages.md) - Complete API reference +- [Client Configuration](../reference/client-config.md) - Advanced configuration diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md new file mode 100644 index 000000000..7d6f9360a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md @@ -0,0 +1,300 @@ +# Multimodal Content Guide + +Working with images, documents, and PDFs in Claude conversations. + +## Image Input + +### Base64 Images + +```python +import base64 + +with open("image.jpg", "rb") as f: + image_data = base64.standard_b64encode(f.read()).decode() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/jpeg", + "data": image_data + } + }, + {"type": "text", "text": "Describe this image"} + ] + }] +) +``` + +### Supported Image Formats + +```python { .api } +# Supported MIME types +"image/jpeg" +"image/png" +"image/gif" +"image/webp" +``` + +### Multiple Images + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img1}}, + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img2}}, + {"type": "text", "text": "Compare these images"} + ] + }] +) +``` + +## PDF Documents + +### Base64 PDF + +```python +import base64 + +with open("document.pdf", "rb") as f: + pdf_data = base64.standard_b64encode(f.read()).decode() + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "base64", + "media_type": "application/pdf", + "data": pdf_data + } + }, + {"type": "text", "text": "Summarize this document"} + ] + }] +) +``` + +### PDF URL + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "url", + "media_type": "application/pdf", + "url": "https://example.com/document.pdf" + } + }, + {"type": "text", "text": "Summarize this document"} + ] + }] +) +``` + +## Plain Text Documents + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + { + "type": "document", + "source": { + "type": "text", + "media_type": "text/plain", + "data": "Long text content..." + } + }, + {"type": "text", "text": "Analyze this text"} + ] + }] +) +``` + +## Mixed Content + +Combine text, images, and documents: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + {"type": "text", "text": "Review this presentation:"}, + {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, + {"type": "text", "text": "Here's the cover image:"}, + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img_data}}, + {"type": "text", "text": "What are the key points?"} + ] + }] +) +``` + +## Image Analysis Tasks + +### Object Detection + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {...}}, + {"type": "text", "text": "List all objects visible in this image"} + ] + }] +) +``` + +### Text Extraction (OCR) + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "image", "source": {...}}, + {"type": "text", "text": "Extract all text from this image"} + ] + }] +) +``` + +### Image Comparison + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "text", "text": "Before:"}, + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": before_img}}, + {"type": "text", "text": "After:"}, + {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": after_img}}, + {"type": "text", "text": "What changed?"} + ] + }] +) +``` + +## Document Analysis Tasks + +### Summarization + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {...}}, + {"type": "text", "text": "Provide a brief summary"} + ] + }] +) +``` + +### Q&A on Documents + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {...}}, + {"type": "text", "text": "What is the main conclusion?"} + ] + }] +) +``` + +### Citation Support (Beta) + +```python +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + citations={"type": "enabled"}, + messages=[{ + "role": "user", + "content": [ + {"type": "document", "source": {...}}, + {"type": "text", "text": "Summarize with citations"} + ] + }] +) + +for block in message.content: + if hasattr(block, 'citations'): + for citation in block.citations: + print(f"Citation: {citation.cited_text}") +``` + +## Best Practices + +### 1. Optimize Image Size + +```python +from PIL import Image +import io +import base64 + +def optimize_image(image_path, max_size=(1024, 1024)): + img = Image.open(image_path) + img.thumbnail(max_size, Image.Resampling.LANCZOS) + + buffer = io.BytesIO() + img.save(buffer, format="JPEG", quality=85) + return base64.standard_b64encode(buffer.getvalue()).decode() + +optimized_data = optimize_image("large_image.jpg") +``` + +### 2. Handle Large PDFs + +For very large PDFs, consider splitting or summarizing sections. + +### 3. Specify Context + +Always provide clear instructions about what you want from the content. + +## See Also + +- [Messages API](../api/messages.md) - Content block types +- [Beta Features](../beta/index.md) - Citations feature +- [Getting Started](./getting-started.md) - Basic usage diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md new file mode 100644 index 000000000..859fd6cf0 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md @@ -0,0 +1,221 @@ +# Streaming Guide + +Advanced patterns for streaming responses from Claude. + +## Basic Streaming + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +print() +``` + +## Event-Based Streaming + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) as stream: + for event in stream: + if event.type == "message_start": + print("Stream started") + elif event.type == "content_block_start": + print(f"\nContent block {event.index} started") + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + elif event.type == "message_stop": + print("\nStream completed") +``` + +## Get Final Message + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "What is 2+2?"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + +# Get complete message after streaming +message = stream.get_final_message() +print(f"\nTokens used: {message.usage.output_tokens}") +``` + +## Stream with Tool Use + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[{ + "name": "get_weather", + "description": "Get weather", + "input_schema": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"] + } + }], + messages=[{"role": "user", "content": "What's the weather in SF?"}] +) as stream: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) + elif event.delta.type == "input_json_delta": + print(event.delta.partial_json, end="") + +message = stream.get_final_message() +``` + +## Async Streaming + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + async with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a haiku"}] + ) as stream: + async for text in stream.text_stream: + print(text, end="", flush=True) + print() + +asyncio.run(main()) +``` + +## Track Token Usage + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write an essay"}] +) as stream: + tokens_used = 0 + + for event in stream: + if event.type == "message_delta": + tokens_used = event.usage.output_tokens + print(f"\nTokens: {tokens_used}", end="\r") + elif event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) +``` + +## Stream with Beta Features + +```python +with client.beta.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=2048, + thinking={"type": "enabled"}, + messages=[{"role": "user", "content": "Explain quantum computing"}] +) as stream: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "thinking_delta": + print(f"[Thinking: {event.delta.thinking}]") + elif event.delta.type == "text_delta": + print(event.delta.text, end="", flush=True) +``` + +## Error Handling + +```python +from anthropic import APIError + +try: + with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] + ) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +except APIError as e: + print(f"\nStream error: {e.message}") +``` + +## Buffered Streaming + +Buffer output for smoother display: + +```python +import time + +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a story"}] +) as stream: + buffer = "" + + for text in stream.text_stream: + buffer += text + + # Flush buffer every 10 characters or at punctuation + if len(buffer) >= 10 or text in ".!?\n": + print(buffer, end="", flush=True) + buffer = "" + time.sleep(0.01) # Smooth animation + + if buffer: # Flush remaining + print(buffer, end="", flush=True) +``` + +## Best Practices + +### 1. Use Context Managers + +Always use `with` statement to ensure proper cleanup: + +```python +with client.messages.stream(...) as stream: + ... +# Stream automatically closed +``` + +### 2. Handle Interruptions + +```python +import signal + +def signal_handler(sig, frame): + print("\nStream interrupted") + stream.close() + sys.exit(0) + +signal.signal(signal.SIGINT, signal_handler) +``` + +### 3. Set Appropriate Timeouts + +```python +import httpx + +client = Anthropic( + timeout=httpx.Timeout(120.0) # 2 minutes for streaming +) +``` + +## See Also + +- [Streaming API](../api/streaming.md) - Complete API reference +- [Messages API](../api/messages.md) - Message creation +- [Beta Features](../beta/index.md) - Extended thinking streaming diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md new file mode 100644 index 000000000..a1a55a3a0 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md @@ -0,0 +1,342 @@ +# Tool Usage Guide + +Comprehensive guide to using tools (function calling) with Claude for building agentic workflows. + +## Tool Basics + +Tools let Claude call functions you define, enabling it to take actions and retrieve information. + +### Manual Tool Definition + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[{ + "name": "get_weather", + "description": "Get weather for a location", + "input_schema": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "City and state"} + }, + "required": ["location"] + } + }], + messages=[{"role": "user", "content": "What's the weather in SF?"}] +) +``` + +### Using Tool Decorators + +```python +from anthropic import beta_tool + +@beta_tool +def get_weather(location: str, unit: str = "fahrenheit") -> dict: + """ + Get weather for a location. + + Args: + location: City and state, e.g. San Francisco, CA + unit: Temperature unit (celsius or fahrenheit) + """ + # Implementation + return {"temperature": 72, "condition": "sunny", "unit": unit} + +message = client.beta.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather], + messages=[{"role": "user", "content": "What's the weather in NYC?"}] +) +``` + +## Tool Execution Flow + +### Manual Execution + +```python +# 1. Send initial request +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + messages=[{"role": "user", "content": "What's the weather in Paris?"}] +) + +# 2. Extract tool use +tool_use = next(block for block in message.content if block.type == "tool_use") + +# 3. Execute function +result = get_weather(location=tool_use.input["location"]) + +# 4. Send result back +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + messages=[ + {"role": "user", "content": "What's the weather in Paris?"}, + {"role": "assistant", "content": message.content}, + { + "role": "user", + "content": [{ + "type": "tool_result", + "tool_use_id": tool_use.id, + "content": str(result) + }] + } + ] +) +``` + +### Automatic Execution with Tool Runner + +```python +@beta_tool +def get_weather(location: str) -> dict: + """Get weather for location.""" + return {"temp": 72, "condition": "sunny"} + +@beta_tool +def get_time(timezone: str = "UTC") -> str: + """Get current time in timezone.""" + from datetime import datetime + return datetime.now().strftime("%H:%M") + +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather, get_time], + messages=[{"role": "user", "content": "What's the weather and time in SF?"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +## Tool Choice Control + +### Auto (Default) + +Let Claude decide whether to use tools: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "auto"}, + messages=[...] +) +``` + +### Force Any Tool + +Require Claude to use at least one tool: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "any"}, + messages=[...] +) +``` + +### Force Specific Tool + +Require Claude to use a specific tool: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "tool", "name": "get_weather"}, + messages=[...] +) +``` + +### Disable All Tools + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={"type": "none"}, + messages=[...] +) +``` + +### Disable Parallel Tool Use + +Force sequential tool calls: + +```python +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + tool_choice={ + "type": "auto", + "disable_parallel_tool_use": True + }, + messages=[...] +) +``` + +## Advanced Patterns + +### Stateful Tools + +```python +class Calculator: + def __init__(self): + self.memory = 0 + + @beta_tool + def calculate(self, expression: str) -> float: + """Evaluate mathematical expression.""" + result = eval(expression) + self.memory = result + return result + + @beta_tool + def recall(self) -> float: + """Recall last calculation result.""" + return self.memory + +calc = Calculator() + +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[calc.calculate, calc.recall], + messages=[{"role": "user", "content": "Calculate 5*8, then add 10"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +### Error Handling in Tools + +```python +@beta_tool +def divide(a: float, b: float) -> float: + """Divide two numbers.""" + if b == 0: + raise ValueError("Cannot divide by zero") + return a / b + +# In tool result +try: + result = divide(10, 0) + tool_result = str(result) + is_error = False +except Exception as e: + tool_result = str(e) + is_error = True + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[...], + messages=[ + ..., + { + "role": "user", + "content": [{ + "type": "tool_result", + "tool_use_id": tool_use.id, + "content": tool_result, + "is_error": is_error + }] + } + ] +) +``` + +### Async Tools + +```python +from anthropic import beta_async_tool + +@beta_async_tool +async def fetch_data(url: str) -> dict: + """Fetch data from URL.""" + async with httpx.AsyncClient() as client: + response = await client.get(url) + return response.json() + +# Use with async tool runner +async for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[fetch_data], + messages=[...] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) +``` + +## Best Practices + +### 1. Clear Tool Descriptions + +```python +@beta_tool +def search_database( + query: str, + limit: int = 10, + category: str | None = None +) -> list[dict]: + """ + Search database for items matching query. + + Args: + query: Search keywords or phrase + limit: Maximum results to return (1-100) + category: Optional category filter (e.g., "electronics", "books") + + Returns: + List of matching items with id, name, price + """ + ... +``` + +### 2. Validate Tool Inputs + +```python +@beta_tool +def set_temperature(degrees: float, unit: str = "celsius") -> dict: + """Set thermostat temperature.""" + if unit not in ["celsius", "fahrenheit"]: + raise ValueError(f"Invalid unit: {unit}") + if degrees < -50 or degrees > 50: + raise ValueError(f"Temperature out of range: {degrees}") + # Set temperature + return {"status": "success", "temperature": degrees, "unit": unit} +``` + +### 3. Return Structured Data + +```python +@beta_tool +def get_user_info(user_id: str) -> dict: + """Get user information.""" + return { + "id": user_id, + "name": "Alice Smith", + "email": "alice@example.com", + "created_at": "2024-01-15" + } +``` + +## See Also + +- [Tool Use API](../api/tools.md) - Complete API reference +- [Beta Features](../beta/index.md) - Tool runner and advanced features +- [Messages API](../api/messages.md) - Message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/index.md b/.tessl/tiles/tessl/pypi-anthropic/docs/index.md new file mode 100644 index 000000000..747b2a9cc --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/index.md @@ -0,0 +1,398 @@ +# Anthropic Python SDK + +The official Python library for the Anthropic REST API, providing type-safe access to Claude AI models with both sync and async support. + +## Package Information + +- **Package Name**: anthropic +- **Package Type**: Python SDK +- **Language**: Python 3.8+ +- **Installation**: `pip install anthropic` +- **Repository**: https://github.com/anthropics/anthropic-sdk-python +- **License**: MIT + +## Installation + +```bash +pip install anthropic +``` + +Platform-specific extras: +- `pip install anthropic[bedrock]` - AWS Bedrock +- `pip install anthropic[vertex]` - Google Vertex AI +- `pip install anthropic[aiohttp]` - Alternative async client + +## Quick Start + +### Basic Message + +```python { .api } +from anthropic import Anthropic + +client = Anthropic() # Reads ANTHROPIC_API_KEY from environment + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello, Claude"}] +) + +print(message.content[0].text) +``` + +### Async Message + +```python { .api } +from anthropic import AsyncAnthropic + +client = AsyncAnthropic() +message = await client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello"}] +) +``` + +### Stream Response + +```python { .api } +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +## Available Models + +**Claude 4.5 (Latest):** +- `claude-opus-4-5-20250929` - Most capable +- `claude-sonnet-4-5-20250929` - Balanced (recommended) + +**Claude 3.5:** +- `claude-3-5-sonnet-20241022` - Previous Sonnet +- `claude-3-5-haiku-20241022` - Fast and cost-effective + +**[→ Complete model list and selection guide](./api/models.md)** + +## Common Tasks + +Choose based on your use case: + +**[Basic Messaging](./common-tasks/basic-messaging.md)** +- Simple text conversations +- System prompts +- Multi-turn conversations +- Temperature control + +**[Multimodal Input](./common-tasks/multimodal-input.md)** +- Image analysis (JPG, PNG, GIF, WebP) +- PDF document processing +- Mixed content (text + images + documents) + +**[Tool Integration](./common-tasks/tool-integration.md)** +- Function calling basics +- Auto-execution with tool_runner +- Async tools +- Error handling in tools + +**[Streaming Responses](./common-tasks/streaming-responses.md)** +- Real-time text streaming +- Event-based processing +- Token usage tracking +- Error handling + +**[Batch Processing](./guides/batch-processing.md)** +- Process thousands of requests +- 50% cost reduction +- High-throughput scenarios + +## API Quick Reference + +Fast lookup for method signatures and parameters: + +**[Messages API](./quick-reference/messages.md)** - Core message creation +- `create()` - Create single message +- `stream()` - Stream message response +- `count_tokens()` - Estimate token usage + +**[Streaming API](./quick-reference/streaming.md)** - Real-time response processing +- `stream()` - Context manager for streaming +- Event types and handling +- Helper methods + +**[Tools API](./quick-reference/tools.md)** - Function calling +- `@beta_tool` decorator +- `tool_runner()` - Auto-execution +- Manual tool handling + +**[Batches API](./quick-reference/batches.md)** - Async batch processing +- `create()` - Submit batch +- `retrieve()` - Check status +- `results()` - Get outputs + +**[Models API](./quick-reference/models.md)** - Model information +- `retrieve()` - Get model details +- `list()` - Browse available models + +## Detailed API Documentation + +In-depth reference with all parameters, types, and examples: + +### Core APIs + +- **[Messages API](./api/messages.md)** - Complete messages API reference with all parameters, types, and examples +- **[Streaming API](./api/streaming.md)** - Detailed streaming architecture, events, and patterns +- **[Tools API](./api/tools.md)** - Function calling with decorators and manual definitions +- **[Batches API](./api/batches.md)** - Batch processing for high-throughput use cases +- **[Models API](./api/models.md)** - Model information and selection +- **[Completions API](./api/completions.md)** - Legacy text completions API (deprecated, use Messages API instead) + +### Implementation Guides + +- **[Getting Started](./guides/getting-started.md)** - Installation, authentication, and first steps +- **[Multimodal Content](./guides/multimodal.md)** - Working with images, documents, and PDFs +- **[Tool Usage](./guides/tool-usage.md)** - Building agentic workflows with function calling +- **[Streaming Guide](./guides/streaming-guide.md)** - Advanced streaming patterns and best practices +- **[Batch Processing](./guides/batch-processing.md)** - Large-scale async message processing +- **[Error Handling](./guides/error-handling.md)** - Robust error management and retry strategies + +### Platform Integrations + +- **[AWS Bedrock](./platforms/bedrock.md)** - Use Claude on AWS infrastructure +- **[Google Vertex AI](./platforms/vertex.md)** - Use Claude on GCP infrastructure +- **[Azure AI Foundry](./platforms/foundry.md)** - Use Claude on Azure infrastructure + +### Reference Documentation + +- **[Client Configuration](./reference/client-config.md)** - Client initialization, timeouts, retries, HTTP options +- **[Type System](./reference/types.md)** - Complete Pydantic type definitions +- **[Error Handling](./reference/errors.md)** - Exception hierarchy and error types +- **[Pagination](./reference/pagination.md)** - List operation pagination patterns +- **[Utilities](./reference/utilities.md)** - Helper functions and utilities + +## Beta Features + +Access experimental capabilities via `client.beta` namespace: + +**[Beta Overview](./beta/index.md)** - Introduction to beta features + +**Message Enhancement Features:** +- **[Extended Thinking](./beta/message-features.md#extended-thinking)** - Long-form reasoning with budget control +- **[Citations](./beta/message-features.md#citations)** - Source attribution for document-based responses +- **[Web Search](./beta/message-features.md#web-search)** - Real-time web information retrieval +- **[Code Execution](./beta/message-features.md#code-execution)** - Python sandbox execution +- **[Computer Use](./beta/message-features.md#computer-use)** - GUI interaction capabilities +- **[MCP Integration](./beta/message-features.md#mcp-integration)** - Model Context Protocol tools + +**[→ All Message Features](./beta/message-features.md)** + +**Resource Management:** +- **[Skills API](./beta/skills.md)** - Create and manage reusable capabilities +- **[Files API](./beta/files.md)** - Upload and manage document files +- **[Beta Batches](./beta/batches.md)** - Batch processing with beta features + +## Client Configuration + +### Basic Setup + +```python { .api } +from anthropic import Anthropic + +# Environment variable (recommended) +client = Anthropic() # Uses ANTHROPIC_API_KEY + +# Explicit API key +client = Anthropic(api_key="your-api-key") + +# Context manager (automatic cleanup) +with Anthropic() as client: + message = client.messages.create(...) +``` + +### Common Configurations + +```python { .api } +import httpx + +# Custom timeout +client = Anthropic(timeout=120.0) + +# Granular timeout +client = Anthropic( + timeout=httpx.Timeout( + connect=10.0, + read=60.0, + write=10.0, + pool=10.0 + ) +) + +# Retry configuration +client = Anthropic(max_retries=5) + +# Custom headers +client = Anthropic( + default_headers={"X-Custom": "value"} +) +``` + +**[→ Complete configuration reference](./reference/client-config.md)** + +## Error Handling + +### Basic Pattern + +```python +from anthropic import APIError, RateLimitError + +try: + message = client.messages.create(...) +except RateLimitError as e: + retry_after = e.response.headers.get("retry-after") + print(f"Rate limited. Retry after {retry_after}s") +except APIError as e: + print(f"API error: {e.message}") +``` + +### Exception Hierarchy + +```python { .api } +AnthropicError +├── APIError +│ ├── APIStatusError +│ │ ├── BadRequestError (400) +│ │ ├── AuthenticationError (401) +│ │ ├── PermissionDeniedError (403) +│ │ ├── NotFoundError (404) +│ │ ├── RateLimitError (429) +│ │ ├── InternalServerError (≥500) +│ ├── APIConnectionError +│ ├── APITimeoutError +│ └── APIResponseValidationError +``` + +**[→ Complete error reference and retry patterns](./reference/errors.md)** + +**[→ Error handling guide with advanced patterns](./guides/error-handling.md)** + +## Environment Variables + +- `ANTHROPIC_API_KEY` - API key for authentication (required) +- `ANTHROPIC_BASE_URL` - Override base URL (optional) +- `ANTHROPIC_AUTH_TOKEN` - Bearer token alternative (optional) + +Platform-specific variables documented in platform guides. + +## SDK Architecture + +### Client Hierarchy + +- **Anthropic / AsyncAnthropic** - Main clients for direct API access +- **AnthropicBedrock / AsyncAnthropicBedrock** - AWS Bedrock integration +- **AnthropicVertex / AsyncAnthropicVertex** - Google Vertex AI integration +- **AnthropicFoundry / AsyncAnthropicFoundry** - Azure AI Foundry integration + +### Resource Structure + +```python { .api } +client.messages # Messages resource + .create() # Create message + .stream() # Stream message + .count_tokens() # Count tokens + .batches # Batches sub-resource + .create() # Create batch + .retrieve() # Get batch status + .list() # List batches + .cancel() # Cancel batch + .delete() # Delete batch + .results() # Get batch results + +client.beta # Beta features namespace + .messages # Beta messages with additional features + .create() # Create with beta features + .stream() # Stream with beta features + .tool_runner() # Auto-execute tools + .skills # Skills management + .files # File management + +client.models # Models information + .retrieve() # Get model info + .list() # List models +``` + +## Type System + +All requests and responses use Pydantic models for type safety: + +```python { .api } +class Message(BaseModel): + id: str + type: Literal["message"] + role: Literal["assistant"] + content: list[ContentBlock] + model: str + stop_reason: StopReason | None + usage: Usage + +ContentBlock = Union[TextBlock, ToolUseBlock] +StopReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] +``` + +**[→ Complete type definitions](./reference/types.md)** + +## Decision Guide for Common Scenarios + +### "I need to send a message to Claude" +→ **[Basic Messaging](./common-tasks/basic-messaging.md)** or **[Messages API](./api/messages.md)** + +### "I need to process images or PDFs" +→ **[Multimodal Input](./common-tasks/multimodal-input.md)** or **[Multimodal Guide](./guides/multimodal.md)** + +### "I need Claude to call functions/use tools" +→ **[Tool Integration](./common-tasks/tool-integration.md)** or **[Tools API](./api/tools.md)** + +### "I need real-time streaming output" +→ **[Streaming Responses](./common-tasks/streaming-responses.md)** or **[Streaming API](./api/streaming.md)** + +### "I need to process thousands of messages" +→ **[Batch Processing Guide](./guides/batch-processing.md)** or **[Batches API](./api/batches.md)** + +### "I'm getting errors" +→ **[Error Reference](./reference/errors.md)** or **[Error Handling Guide](./guides/error-handling.md)** + +### "I need extended reasoning/thinking" +→ **[Beta Overview](./beta/index.md)** → **[Extended Thinking](./beta/message-features.md#extended-thinking)** + +### "I need web search or code execution" +→ **[Beta Overview](./beta/index.md)** → **[Message Features](./beta/message-features.md)** + +### "I'm using AWS/GCP/Azure" +→ **[Platform Integrations](#platform-integrations)** → Choose your platform + +## Package Constants + +```python { .api } +# Client Configuration Constants +DEFAULT_TIMEOUT: float = 600.0 # 10 minutes default timeout for requests +DEFAULT_MAX_RETRIES: int = 2 # Default number of retry attempts +DEFAULT_CONNECTION_LIMITS: httpx.Limits # Default HTTP connection pool limits + +# Legacy Text Completion Prompt Constants +HUMAN_PROMPT: str = "\n\nHuman:" # Legacy prompt marker for human messages +AI_PROMPT: str = "\n\nAssistant:" # Legacy prompt marker for AI responses + +# Sentinel Values +NOT_GIVEN: NotGiven # Sentinel indicating parameter not provided +``` + +**Note**: `HUMAN_PROMPT` and `AI_PROMPT` are legacy constants for the deprecated Text Completions API. Use the Messages API instead for new applications. + +## Support Resources + +- **API Documentation**: https://docs.anthropic.com +- **SDK Repository**: https://github.com/anthropics/anthropic-sdk-python +- **Model Pricing**: https://anthropic.com/pricing +- **Rate Limits**: https://docs.anthropic.com/rate-limits diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md new file mode 100644 index 000000000..c08df71b7 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md @@ -0,0 +1,148 @@ +# AWS Bedrock Integration + +Use Claude models via AWS Bedrock with automatic authentication and region configuration. + +## Installation + +```bash +pip install anthropic[bedrock] +``` + +## Client Initialization + +```python { .api } +class AnthropicBedrock: + """Synchronous client for Claude on AWS Bedrock.""" + + def __init__( + self, + *, + aws_access_key: str | None = None, + aws_secret_key: str | None = None, + aws_session_token: str | None = None, + aws_region: str | None = None, + aws_profile: str | None = None, + timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: dict[str, str] | None = None, + http_client: httpx.Client | None = None, + ): + """ + Initialize Bedrock client. + + Parameters: + aws_access_key: AWS access key ID (or AWS_ACCESS_KEY_ID env var) + aws_secret_key: AWS secret access key (or AWS_SECRET_ACCESS_KEY env var) + aws_session_token: AWS session token (or AWS_SESSION_TOKEN env var) + aws_region: AWS region (or AWS_REGION env var, default: us-east-1) + aws_profile: AWS profile from ~/.aws/credentials + timeout: Request timeout + max_retries: Maximum retry attempts + default_headers: Custom headers + http_client: Custom httpx.Client + """ + ... + +class AsyncAnthropicBedrock: + """Asynchronous client for Claude on AWS Bedrock.""" + # Same parameters as AnthropicBedrock + ... +``` + +## Bedrock Model Identifiers + +```python { .api } +# Claude 3.5 on Bedrock +"anthropic.claude-3-5-sonnet-20241022-v2:0" +"anthropic.claude-3-5-sonnet-20240620-v1:0" +"anthropic.claude-3-5-haiku-20241022-v1:0" + +# Claude 3 on Bedrock +"anthropic.claude-3-opus-20240229-v1:0" +"anthropic.claude-3-sonnet-20240229-v1:0" +"anthropic.claude-3-haiku-20240307-v1:0" +``` + +## Quick Examples + +### Basic Usage + +```python +from anthropic import AnthropicBedrock + +client = AnthropicBedrock() # Uses environment variables + +message = client.messages.create( + model="anthropic.claude-3-5-sonnet-20241022-v2:0", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello!"}] +) +``` + +### Explicit Credentials + +```python +client = AnthropicBedrock( + aws_access_key="AKIA...", + aws_secret_key="wJal...", + aws_region="us-east-1" +) +``` + +### Using AWS Profile + +```python +client = AnthropicBedrock( + aws_profile="production", + aws_region="us-west-2" +) +``` + +### Streaming + +```python +with client.messages.stream( + model="anthropic.claude-3-5-sonnet-20241022-v2:0", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +### Multi-Region Deployment + +```python +regions = ["us-east-1", "us-west-2", "eu-west-1"] + +for region in regions: + client = AnthropicBedrock(aws_region=region) + message = client.messages.create(...) + client.close() +``` + +### Async Client + +```python +import asyncio +from anthropic import AsyncAnthropicBedrock + +async def main(): + client = AsyncAnthropicBedrock(aws_region="us-east-1") + message = await client.messages.create(...) + await client.close() + +asyncio.run(main()) +``` + +## Environment Variables + +- `AWS_ACCESS_KEY_ID` - AWS access key +- `AWS_SECRET_ACCESS_KEY` - AWS secret key +- `AWS_SESSION_TOKEN` - AWS session token +- `AWS_REGION` - AWS region (default: us-east-1) + +## See Also + +- [Messages API](../api/messages.md) - Core message creation +- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md new file mode 100644 index 000000000..6868c08bc --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md @@ -0,0 +1,144 @@ +# Azure AI Foundry Integration + +Access Claude models via Azure AI Foundry with API key or Azure AD authentication. + +## Overview + +Azure AI Foundry provides access to Claude through Microsoft's Azure AI platform with: +- Azure API key authentication +- Azure Active Directory (AAD) token provider authentication +- Automatic base URL construction from resource names + +## Client Initialization + +```python { .api } +class AnthropicFoundry: + def __init__( + self, + *, + resource: str | None = None, + api_key: str | None = None, + azure_ad_token_provider: Callable[[], str] | None = None, + base_url: str | None = None, + timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: dict[str, str] | None = None, + http_client: httpx.Client | None = None, + ): + """ + Initialize Azure AI Foundry client. + + Parameters: + resource: Azure resource name (e.g., "my-resource" for + https://my-resource.services.ai.azure.com/anthropic/) + api_key: Azure API key (or ANTHROPIC_FOUNDRY_API_KEY env var) + azure_ad_token_provider: Function returning Azure AD token + base_url: Full base URL (mutually exclusive with resource) + timeout: Request timeout + max_retries: Maximum retry attempts + default_headers: Custom headers + http_client: Custom httpx.Client + + Environment Variables: + ANTHROPIC_FOUNDRY_API_KEY: Default API key + ANTHROPIC_FOUNDRY_RESOURCE: Default resource name + ANTHROPIC_FOUNDRY_BASE_URL: Default base URL + + Note: + Must provide either api_key or azure_ad_token_provider. + Must provide either resource or base_url. + """ + ... + +class AsyncAnthropicFoundry: + # Same parameters, but azure_ad_token_provider can return Awaitable[str] + ... +``` + +## Quick Examples + +### API Key Authentication + +```python +from anthropic import AnthropicFoundry + +client = AnthropicFoundry( + resource="my-resource", + api_key="your-api-key" +) + +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello!"}] +) +``` + +### Azure AD Token Provider + +```python +def get_azure_ad_token(): + """Fetch Azure AD token using your preferred method.""" + # Implement token retrieval + return "your-azure-ad-token" + +client = AnthropicFoundry( + resource="my-resource", + azure_ad_token_provider=get_azure_ad_token +) +``` + +### Using Base URL Directly + +```python +client = AnthropicFoundry( + base_url="https://my-resource.services.ai.azure.com/anthropic/", + api_key="your-api-key" +) +``` + +### Async Client + +```python +import asyncio +from anthropic import AsyncAnthropicFoundry + +async def main(): + client = AsyncAnthropicFoundry( + resource="my-resource", + api_key="your-api-key" + ) + message = await client.messages.create(...) + +asyncio.run(main()) +``` + +### Streaming + +```python +with client.messages.stream( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a haiku"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +## Limitations + +The Azure AI Foundry integration has the following limitations: +1. **No Models API**: `client.models` resource not available +2. **No Message Batches**: `client.messages.batches` resource not available +3. **No Beta Batches**: `client.beta.messages.batches` resource not available + +## Environment Variables + +- `ANTHROPIC_FOUNDRY_API_KEY` - Default API key +- `ANTHROPIC_FOUNDRY_RESOURCE` - Default resource name +- `ANTHROPIC_FOUNDRY_BASE_URL` - Default base URL + +## See Also + +- [Messages API](../api/messages.md) - Core message creation +- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md new file mode 100644 index 000000000..3756993eb --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md @@ -0,0 +1,157 @@ +# Google Vertex AI Integration + +Access Claude models on Google Cloud Platform with Vertex AI integration. + +## Installation + +```bash +pip install anthropic[vertex] +``` + +## Client Initialization + +```python { .api } +class AnthropicVertex: + """Synchronous client for Claude on Google Vertex AI.""" + + def __init__( + self, + *, + project_id: str | None = None, + region: str | None = None, + timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: dict[str, str] | None = None, + http_client: httpx.Client | None = None, + ): + """ + Initialize Vertex AI client. + + Parameters: + project_id: GCP project ID (or CLOUD_ML_PROJECT_ID/GOOGLE_CLOUD_PROJECT env var) + region: GCP region (or CLOUD_ML_REGION env var, default: us-east5) + timeout: Request timeout + max_retries: Maximum retry attempts + default_headers: Custom headers + http_client: Custom httpx.Client + """ + ... + +class AsyncAnthropicVertex: + """Asynchronous client for Claude on Google Vertex AI.""" + # Same parameters as AnthropicVertex + ... +``` + +## Vertex Model Identifiers + +```python { .api } +# Claude models on Vertex AI +"claude-3-5-sonnet-v2@20241022" +"claude-3-5-sonnet@20240620" +"claude-3-5-haiku@20241022" +"claude-3-opus@20240229" +"claude-3-sonnet@20240229" +"claude-3-haiku@20240307" +``` + +## Quick Examples + +### Basic Usage + +```python +from anthropic import AnthropicVertex + +client = AnthropicVertex( + project_id="my-gcp-project", + region="us-east5" +) + +message = client.messages.create( + model="claude-3-5-sonnet-v2@20241022", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello!"}] +) +``` + +### Using Environment Variables + +```python +# Set: CLOUD_ML_PROJECT_ID=my-gcp-project +# Set: CLOUD_ML_REGION=us-east5 + +client = AnthropicVertex() # Automatically uses env vars +``` + +### Using Application Default Credentials + +```python +# First authenticate: gcloud auth application-default login + +client = AnthropicVertex( + project_id="my-gcp-project", + region="us-east5" +) +``` + +### Streaming + +```python +with client.messages.stream( + model="claude-3-5-sonnet-v2@20241022", + max_tokens=1024, + messages=[{"role": "user", "content": "Write a story"}] +) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) +``` + +### Multi-Region Deployment + +```python +regions = ["us-east5", "europe-west1", "asia-southeast1"] + +for region in regions: + client = AnthropicVertex( + project_id="my-gcp-project", + region=region + ) + message = client.messages.create(...) + client.close() +``` + +### Async Client + +```python +import asyncio +from anthropic import AsyncAnthropicVertex + +async def main(): + client = AsyncAnthropicVertex( + project_id="my-gcp-project", + region="us-east5" + ) + message = await client.messages.create(...) + await client.close() + +asyncio.run(main()) +``` + +## Environment Variables + +- `CLOUD_ML_PROJECT_ID` or `GOOGLE_CLOUD_PROJECT` - GCP project ID +- `CLOUD_ML_REGION` - GCP region (default: us-east5) +- `GOOGLE_APPLICATION_CREDENTIALS` - Path to service account key JSON + +## Available Regions + +- `us-east5` +- `us-central1` +- `europe-west1` +- `europe-west4` +- `asia-southeast1` + +## See Also + +- [Messages API](../api/messages.md) - Core message creation +- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md new file mode 100644 index 000000000..3e9dfd47b --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md @@ -0,0 +1,157 @@ +# Batches API - Quick Reference + +Compact API signatures for batch processing. For examples, see **[Batches API Reference](../api/batches.md)**. + +## create() + +```python { .api } +def create( + self, + *, + requests: list[MessageBatchIndividualRequest], # Required: Batch requests + **kwargs +) -> MessageBatch +``` + +**Async:** `async def create(...) -> MessageBatch` + +## retrieve() + +```python { .api } +def retrieve( + self, + message_batch_id: str, # Required: Batch ID + **kwargs +) -> MessageBatch +``` + +**Async:** `async def retrieve(...) -> MessageBatch` + +## list() + +```python { .api } +def list( + self, + *, + before_id: str = NOT_GIVEN, + after_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + **kwargs +) -> SyncPage[MessageBatch] +``` + +**Async:** `async def list(...) -> AsyncPage[MessageBatch]` + +## cancel() + +```python { .api } +def cancel( + self, + message_batch_id: str, # Required: Batch ID to cancel + **kwargs +) -> MessageBatch +``` + +**Async:** `async def cancel(...) -> MessageBatch` + +## delete() + +```python { .api } +def delete( + self, + message_batch_id: str, # Required: Batch ID to delete + **kwargs +) -> DeletedMessageBatch +``` + +**Async:** `async def delete(...) -> DeletedMessageBatch` + +## results() + +```python { .api } +def results( + self, + message_batch_id: str, # Required: Batch ID + **kwargs +) -> JSONLDecoder[MessageBatchIndividualResponse]: + """Stream batch results as JSONL""" + ... +``` + +**Async:** `async def results(...) -> AsyncJSONLDecoder[MessageBatchIndividualResponse]` + +## Key Types + +```python { .api } +class MessageBatchIndividualRequest(TypedDict): + custom_id: str # Your request identifier + params: MessageCreateParams # Same as messages.create() params + +class MessageBatch(BaseModel): + id: str + type: Literal["message_batch"] + processing_status: Literal["in_progress", "canceling", "ended"] + request_counts: MessageBatchRequestCounts + ended_at: str | None + created_at: str + expires_at: str + cancel_initiated_at: str | None + results_url: str | None + +class MessageBatchRequestCounts(BaseModel): + processing: int + succeeded: int + errored: int + canceled: int + expired: int + +class MessageBatchIndividualResponse(BaseModel): + custom_id: str + result: MessageBatchSucceededResult | MessageBatchErroredResult + +class MessageBatchSucceededResult(BaseModel): + type: Literal["succeeded"] + message: Message + +class MessageBatchErroredResult(BaseModel): + type: Literal["errored"] + error: ErrorObject +``` + +## Common Patterns + +```python +# Create batch +batch = client.messages.batches.create( + requests=[ + { + "custom_id": "req-1", + "params": { + "model": "claude-sonnet-4-5-20250929", + "max_tokens": 1024, + "messages": [{"role": "user", "content": "Hello"}] + } + } + ] +) + +# Poll until complete +import time +while True: + batch = client.messages.batches.retrieve(batch.id) + if batch.processing_status == "ended": + break + time.sleep(60) + +# Process results +for response in client.messages.batches.results(batch.id): + if response.result.type == "succeeded": + print(response.result.message.content[0].text) + elif response.result.type == "errored": + print(f"Error: {response.result.error.message}") +``` + +## See Also + +- **[Complete Batches Documentation](../api/batches.md)** - Full details and examples +- **[Batch Processing Guide](../guides/batch-processing.md)** - Advanced patterns and best practices diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md new file mode 100644 index 000000000..57f4c2e25 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md @@ -0,0 +1,97 @@ +# Messages API - Quick Reference + +Compact API signatures for the Messages API. For examples and detailed documentation, see **[Messages API Reference](../api/messages.md)**. + +## create() + +```python { .api } +def create( + self, + *, + model: str, # Required: Model ID + messages: list[MessageParam], # Required: Conversation messages + max_tokens: int, # Required: Max generation tokens + system: str | list[TextBlockParam] = NOT_GIVEN, # System prompt + metadata: MetadataParam = NOT_GIVEN, # Request metadata (user_id) + stop_sequences: list[str] = NOT_GIVEN, # Custom stop sequences (max 4) + stream: bool = False, # Enable streaming (use stream() instead) + temperature: float = NOT_GIVEN, # Sampling temperature 0.0-1.0 + top_p: float = NOT_GIVEN, # Nucleus sampling 0.0-1.0 + top_k: int = NOT_GIVEN, # Top-k sampling + tools: list[ToolParam] = NOT_GIVEN, # Available tools + tool_choice: ToolChoice = NOT_GIVEN, # Tool selection control + service_tier: Literal["auto", "standard_only"] = NOT_GIVEN, # Service tier + thinking: ThinkingConfigParam = NOT_GIVEN, # Extended thinking (beta) +) -> Message +``` + +**Async:** `async def create(...) -> Message` + +**Raises:** `BadRequestError`, `AuthenticationError`, `RateLimitError`, `InternalServerError` + +## stream() + +```python { .api } +def stream( + self, + *, + model: str, # Required: Model ID + messages: list[MessageParam], # Required: Conversation messages + max_tokens: int, # Required: Max generation tokens + **kwargs # All create() parameters supported +) -> MessageStreamManager +``` + +**Returns:** Context manager with `.text_stream`, `.get_final_message()`, `.get_final_text()` + +**Async:** `async def stream(...) -> AsyncMessageStreamManager` + +## count_tokens() + +```python { .api } +def count_tokens( + self, + *, + model: str, # Required: Model ID + messages: list[MessageParam], # Required: Messages to count + system: str | list[TextBlockParam] = NOT_GIVEN, # System prompt + tools: list[ToolParam] = NOT_GIVEN, # Tools to include + tool_choice: ToolChoice = NOT_GIVEN, # Tool choice config + thinking: ThinkingConfigParam = NOT_GIVEN, # Thinking config +) -> MessageTokensCount +``` + +**Returns:** `MessageTokensCount` with `.input_tokens` + +**Async:** `async def count_tokens(...) -> MessageTokensCount` + +## Key Types + +```python { .api } +class MessageParam(TypedDict): + role: Literal["user", "assistant"] + content: str | list[ContentBlockParam] + +class Message(BaseModel): + id: str + type: Literal["message"] + role: Literal["assistant"] + content: list[ContentBlock] # TextBlock | ToolUseBlock + model: str + stop_reason: StopReason | None + usage: Usage + +class Usage(BaseModel): + input_tokens: int + output_tokens: int + cache_creation_input_tokens: int | None + cache_read_input_tokens: int | None + +StopReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] +``` + +## See Also + +- **[Complete API Documentation](../api/messages.md)** - Full details with examples +- **[Basic Messaging Tasks](../common-tasks/basic-messaging.md)** - Task-oriented guide +- **[Type System Reference](../reference/types.md)** - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md new file mode 100644 index 000000000..2c27bdf84 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md @@ -0,0 +1,98 @@ +# Models API - Quick Reference + +Compact API signatures for model information. For examples, see **[Models API Reference](../api/models.md)**. + +## retrieve() + +```python { .api } +def retrieve( + self, + model_id: str, # Required: Model identifier + **kwargs +) -> ModelInfo +``` + +**Async:** `async def retrieve(...) -> ModelInfo` + +## list() + +```python { .api } +def list( + self, + *, + before_id: str = NOT_GIVEN, + after_id: str = NOT_GIVEN, + limit: int = NOT_GIVEN, + **kwargs +) -> SyncPage[ModelInfo] +``` + +**Async:** `async def list(...) -> AsyncPage[ModelInfo]` + +## Response Type + +```python { .api } +class ModelInfo(BaseModel): + id: str # Model identifier + type: Literal["model"] + display_name: str # Human-readable name + created_at: str # ISO 8601 timestamp +``` + +## Available Model IDs + +```python { .api } +# Claude 4.5 (Latest) +"claude-opus-4-5-20250929" # Most capable model +"claude-sonnet-4-5-20250929" # Balanced intelligence and speed + +# Claude 3.5 +"claude-3-5-sonnet-20241022" # Previous Sonnet version +"claude-3-5-sonnet-20240620" # Earlier Sonnet version +"claude-3-5-haiku-20241022" # Fast, cost-effective + +# Claude 3 +"claude-3-opus-20240229" # Powerful, intelligent +"claude-3-sonnet-20240229" # Balanced +"claude-3-haiku-20240307" # Fast and efficient + +# Legacy (Claude 2) +"claude-2.1" +"claude-2.0" +"claude-instant-1.2" +``` + +## Common Patterns + +```python +# Get specific model info +model = client.models.retrieve("claude-sonnet-4-5-20250929") +print(f"{model.display_name} created: {model.created_at}") + +# List all models +for model in client.models.list(): + print(f"{model.id}: {model.display_name}") + +# Check if model exists +from anthropic import NotFoundError + +def model_exists(model_id: str) -> bool: + try: + client.models.retrieve(model_id) + return True + except NotFoundError: + return False +``` + +## Model Selection Guide + +**Choose based on requirements:** + +- **claude-opus-4-5-20250929** - Complex tasks requiring maximum capability and reasoning +- **claude-sonnet-4-5-20250929** - Balanced performance for most use cases (recommended) +- **claude-3-5-haiku-20241022** - Fast responses and cost-effective for simple tasks +- **claude-3-5-sonnet-20241022** - Previous Sonnet version for compatibility + +## See Also + +- **[Complete Models Documentation](../api/models.md)** - Full details and selection examples diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md new file mode 100644 index 000000000..568c3325b --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md @@ -0,0 +1,115 @@ +# Streaming API - Quick Reference + +Compact API signatures for streaming. For examples and patterns, see **[Streaming API Reference](../api/streaming.md)**. + +## stream() + +```python { .api } +def stream( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + **kwargs # All messages.create() parameters supported +) -> MessageStreamManager +``` + +**Async:** `async def stream(...) -> AsyncMessageStreamManager` + +## MessageStreamManager + +```python { .api } +class MessageStreamManager: + def __enter__(self) -> MessageStream + def __exit__(self, exc_type, exc_val, exc_tb) -> None +``` + +## MessageStream + +```python { .api } +class MessageStream: + def __iter__(self) -> Iterator[MessageStreamEvent] + + @property + def text_stream(self) -> Iterator[str]: + """Iterate over text deltas only""" + ... + + @property + def current_message_snapshot(self) -> Message: + """Current accumulated message during streaming""" + ... + + def get_final_message(self) -> Message: + """Complete message after stream ends""" + ... + + def get_final_text(self) -> str: + """Accumulated text after stream ends""" + ... +``` + +**Async:** `AsyncMessageStream` with `async` versions of all methods + +## Stream Events + +```python { .api } +MessageStreamEvent = Union[ + MessageStartEvent, # Stream started + MessageDeltaEvent, # Usage/stop_reason updated + MessageStopEvent, # Stream completed + ContentBlockStartEvent, # New content block + ContentBlockDeltaEvent, # Content delta + ContentBlockStopEvent, # Block completed +] + +class MessageStartEvent(BaseModel): + type: Literal["message_start"] + message: Message # Initial skeleton + +class ContentBlockDeltaEvent(BaseModel): + type: Literal["content_block_delta"] + index: int + delta: ContentBlockDelta # TextDelta | InputJSONDelta + +class TextDelta(BaseModel): + type: Literal["text_delta"] + text: str # Incremental text to append + +class MessageDeltaEvent(BaseModel): + type: Literal["message_delta"] + delta: MessageDelta # stop_reason, stop_sequence + usage: MessageDeltaUsage # output_tokens + +class MessageStopEvent(BaseModel): + type: Literal["message_stop"] +``` + +## Common Patterns + +```python +# Text streaming +with client.messages.stream(...) as stream: + for text in stream.text_stream: + print(text, end="", flush=True) + +# Event processing +with client.messages.stream(...) as stream: + for event in stream: + if event.type == "content_block_delta": + if event.delta.type == "text_delta": + print(event.delta.text, end="") + +# Get final message +with client.messages.stream(...) as stream: + for text in stream.text_stream: + print(text, end="") +message = stream.get_final_message() +``` + +## See Also + +- **[Complete Streaming Documentation](../api/streaming.md)** - All event types and patterns +- **[Streaming Tasks Guide](../common-tasks/streaming-responses.md)** - Task-oriented examples +- **[Streaming Guide](../guides/streaming-guide.md)** - Advanced patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md new file mode 100644 index 000000000..afc548415 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md @@ -0,0 +1,159 @@ +# Tools API - Quick Reference + +Compact API signatures for tool use (function calling). For examples, see **[Tools API Reference](../api/tools.md)**. + +## Decorators + +```python { .api } +def beta_tool(func: Callable) -> BetaFunctionTool: + """ + Convert sync function to tool with auto-generated schema. + Function must have type hints and docstring. + """ + ... + +def beta_async_tool(func: Callable) -> BetaAsyncFunctionTool: + """ + Convert async function to tool with auto-generated schema. + Function must have type hints and docstring. + """ + ... +``` + +## Tool Runner + +```python { .api } +def tool_runner( + self, + *, + model: str, + messages: list[MessageParam], + max_tokens: int, + tools: list[ToolParam | BetaFunctionTool], + tool_choice: ToolChoice = NOT_GIVEN, + **kwargs # Additional messages.create() parameters +) -> Iterator[BetaMessage]: + """ + Auto-execute tools in agentic loop. + Yields message after each turn until stop_reason == "end_turn". + """ + ... +``` + +**Async:** `async def tool_runner(...) -> AsyncIterator[BetaMessage]` + +## Tool Types + +```python { .api } +class ToolParam(TypedDict): + name: str # Tool identifier + description: str # Tool purpose (for Claude) + input_schema: dict[str, Any] # JSON Schema (draft 2020-12) + cache_control: NotRequired[CacheControlEphemeral] + +ToolChoice = Union[ + ToolChoiceAuto, # Let Claude decide (default) + ToolChoiceAny, # Force Claude to use any tool + ToolChoiceNone, # Disable tools + ToolChoiceTool, # Force specific tool +] + +class ToolChoiceAuto(TypedDict): + type: Literal["auto"] + disable_parallel_tool_use: NotRequired[bool] + +class ToolChoiceAny(TypedDict): + type: Literal["any"] + disable_parallel_tool_use: NotRequired[bool] + +class ToolChoiceTool(TypedDict): + type: Literal["tool"] + name: str # Tool name to force + disable_parallel_tool_use: NotRequired[bool] +``` + +## Response Types + +```python { .api } +class ToolUseBlock(BaseModel): + """Tool invocation in assistant response""" + type: Literal["tool_use"] + id: str # Use in tool_result + name: str # Tool name + input: dict[str, Any] # Tool parameters + +class ToolResultBlockParam(TypedDict): + """Tool result in user message""" + type: Literal["tool_result"] + tool_use_id: str # ID from ToolUseBlock + content: NotRequired[str | list[TextBlockParam | ImageBlockParam]] + is_error: NotRequired[bool] # True if tool execution failed + cache_control: NotRequired[CacheControlEphemeral] +``` + +## Function Tool Classes + +```python { .api } +class BetaFunctionTool: + name: str + description: str + input_schema: dict[str, Any] + func: Callable + + def __call__(self, **kwargs) -> Any: ... + def to_param(self) -> ToolParam: ... + +class BetaAsyncFunctionTool: + name: str + description: str + input_schema: dict[str, Any] + func: Callable + + async def __call__(self, **kwargs) -> Any: ... + def to_param(self) -> ToolParam: ... +``` + +## Common Patterns + +```python +# Decorator usage +@beta_tool +def get_weather(location: str, unit: str = "fahrenheit") -> dict: + """Get weather for location. + + Args: + location: City and state + unit: Temperature unit + """ + return {"temp": 72, "unit": unit} + +# Tool runner (auto-execution) +for message in client.beta.messages.tool_runner( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather], + messages=[{"role": "user", "content": "What's the weather in NYC?"}] +): + if message.stop_reason == "end_turn": + print(message.content[0].text) + +# Manual tool handling +message = client.messages.create( + model="claude-sonnet-4-5-20250929", + max_tokens=1024, + tools=[get_weather.to_param()], + messages=[...] +) + +# Extract tool use +for block in message.content: + if block.type == "tool_use": + result = get_weather(**block.input) + # Send result back in next request +``` + +## See Also + +- **[Complete Tools Documentation](../api/tools.md)** - Full details and examples +- **[Tool Integration Tasks](../common-tasks/tool-integration.md)** - Task-oriented guide +- **[Tool Usage Guide](../guides/tool-usage.md)** - Advanced patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md new file mode 100644 index 000000000..52b295c69 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md @@ -0,0 +1,305 @@ +# Client Configuration Reference + +Initialize and configure Anthropic API clients for synchronous and asynchronous operations. + +## Synchronous Client + +```python { .api } +class Anthropic: + """Synchronous client for Anthropic API.""" + + def __init__( + self, + *, + api_key: str | None = None, + auth_token: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: dict[str, str] | None = None, + default_query: dict[str, object] | None = None, + http_client: httpx.Client | None = None, + ): + """ + Initialize Anthropic client. + + Parameters: + api_key: API key (defaults to ANTHROPIC_API_KEY env var) + auth_token: Bearer token (alternative to api_key) + base_url: Override base URL (defaults to ANTHROPIC_BASE_URL env var) + timeout: Request timeout (default: 600s) + max_retries: Maximum retry attempts (default: 2) + default_headers: Headers added to all requests + default_query: Query parameters added to all requests + http_client: Custom httpx.Client instance + """ + ... + + def close(self) -> None: + """Close the underlying HTTP client.""" + ... + + def __enter__(self) -> Anthropic: + """Context manager entry.""" + ... + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + """Context manager exit.""" + ... + + @property + def messages(self) -> Messages: + """Access Messages resource.""" + ... + + @property + def beta(self) -> Beta: + """Access Beta resources.""" + ... + + @property + def models(self) -> Models: + """Access Models resource.""" + ... +``` + +## Asynchronous Client + +```python { .api } +class AsyncAnthropic: + """Asynchronous client for Anthropic API.""" + + def __init__( + self, + *, + api_key: str | None = None, + auth_token: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: dict[str, str] | None = None, + default_query: dict[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + ): + """Initialize async Anthropic client. Same parameters as Anthropic.""" + ... + + async def close(self) -> None: + """Close the underlying HTTP client.""" + ... + + async def __aenter__(self) -> AsyncAnthropic: + """Async context manager entry.""" + ... + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Async context manager exit.""" + ... +``` + +## Constants + +```python { .api } +DEFAULT_TIMEOUT: float = 600.0 # 10 minutes +DEFAULT_MAX_RETRIES: int = 2 +``` + +## Quick Examples + +### Basic Initialization + +```python +from anthropic import Anthropic + +# Using environment variable ANTHROPIC_API_KEY +client = Anthropic() + +# Explicit API key +client = Anthropic(api_key="your-api-key") +``` + +### Context Manager + +```python +with Anthropic() as client: + message = client.messages.create(...) +# Client automatically closed +``` + +### Custom Timeout + +```python +import httpx + +# Single timeout value (applies to all) +client = Anthropic(timeout=120.0) + +# Granular timeout control +client = Anthropic( + timeout=httpx.Timeout( + connect=10.0, # Connection timeout + read=60.0, # Read timeout + write=10.0, # Write timeout + pool=10.0, # Pool timeout + ) +) +``` + +### Retry Configuration + +```python +# Increase retries +client = Anthropic(max_retries=5) + +# Disable retries +client = Anthropic(max_retries=0) +``` + +### Custom Headers + +```python +client = Anthropic( + default_headers={ + "X-Custom-Header": "value", + "User-Agent": "MyApp/1.0", + } +) +``` + +### Custom Base URL + +```python +# Development environment +client = Anthropic( + base_url="https://api.dev.anthropic.com" +) + +# Using environment variable ANTHROPIC_BASE_URL +import os +os.environ["ANTHROPIC_BASE_URL"] = "https://api.dev.anthropic.com" +client = Anthropic() +``` + +### Custom HTTP Client + +```python +import httpx + +# Custom httpx client +http_client = httpx.Client( + proxy="http://proxy.example.com:8080", + limits=httpx.Limits(max_connections=100), +) + +client = Anthropic(http_client=http_client) +``` + +### Async Client + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + message = await client.messages.create(...) + await client.close() + +asyncio.run(main()) +``` + +### Async Context Manager + +```python +async def main(): + async with AsyncAnthropic() as client: + message = await client.messages.create(...) + # Client automatically closed +``` + +### Bearer Token Authentication + +```python +client = Anthropic(auth_token="your-bearer-token") +``` + +### HTTP Client with Proxy + +```python +import httpx + +client = Anthropic( + http_client=httpx.Client( + proxy="http://proxy.example.com:8080" + ) +) +``` + +### Connection Pool Configuration + +```python +import httpx + +client = Anthropic( + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=100, + max_keepalive_connections=20, + ) + ) +) +``` + +### Request Per-Call Options + +```python +# Override timeout for specific request +message = client.messages.with_options(timeout=120.0).create(...) + +# Override headers +message = client.messages.with_options( + headers={"X-Request-ID": "abc123"} +).create(...) + +# Override max retries +message = client.messages.with_options(max_retries=5).create(...) +``` + +## HTTP Client Factories + +```python { .api } +class DefaultHttpxClient: + """Default synchronous HTTP client using httpx.""" + def __init__( + self, + *, + proxy: str | httpx.Proxy | None = None, + transport: httpx.HTTPTransport | None = None, + **kwargs + ): ... + +class DefaultAsyncHttpxClient: + """Default asynchronous HTTP client using httpx.""" + def __init__( + self, + *, + proxy: str | httpx.Proxy | None = None, + transport: httpx.AsyncHTTPTransport | None = None, + **kwargs + ): ... + +class DefaultAioHttpClient: + """Alternative async client using aiohttp (better concurrency).""" + def __init__(self, **kwargs): ... +``` + +## Environment Variables + +- `ANTHROPIC_API_KEY` - API key for authentication +- `ANTHROPIC_BASE_URL` - Override base URL +- `ANTHROPIC_AUTH_TOKEN` - Bearer token (alternative to API key) + +## See Also + +- [Error Handling](./errors.md) - Exception handling and retry logic +- [Utilities](./utilities.md) - HTTP client utilities diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md new file mode 100644 index 000000000..60f490e56 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md @@ -0,0 +1,287 @@ +# Error Handling Reference + +Complete exception hierarchy and error handling patterns for the Anthropic Python SDK. + +## Exception Hierarchy + +```python { .api } +AnthropicError +├── APIError +│ ├── APIStatusError +│ │ ├── BadRequestError (400) +│ │ ├── AuthenticationError (401) +│ │ ├── PermissionDeniedError (403) +│ │ ├── NotFoundError (404) +│ │ ├── ConflictError (409) +│ │ ├── RequestTooLargeError (413) +│ │ ├── UnprocessableEntityError (422) +│ │ ├── RateLimitError (429) +│ │ ├── InternalServerError (≥500) +│ │ ├── ServiceUnavailableError (503) +│ │ ├── DeadlineExceededError (504) +│ │ └── OverloadedError (529) +│ ├── APIConnectionError +│ ├── APITimeoutError +│ └── APIResponseValidationError +``` + +## Exception Classes + +### Base Exceptions + +```python { .api } +class AnthropicError(Exception): + """Base exception for all Anthropic errors.""" + ... + +class APIError(AnthropicError): + """Base for all API-related errors.""" + message: str + request: httpx.Request | None + body: object | None +``` + +### HTTP Status Errors + +```python { .api } +class APIStatusError(APIError): + """HTTP status code error.""" + response: httpx.Response + status_code: int + request_id: str | None + +class BadRequestError(APIStatusError): + """400 - Invalid request.""" + ... + +class AuthenticationError(APIStatusError): + """401 - Invalid API key.""" + ... + +class PermissionDeniedError(APIStatusError): + """403 - Insufficient permissions.""" + ... + +class NotFoundError(APIStatusError): + """404 - Resource not found.""" + ... + +class ConflictError(APIStatusError): + """409 - Request conflicts with current state.""" + ... + +class RequestTooLargeError(APIStatusError): + """413 - Request payload too large.""" + ... + +class UnprocessableEntityError(APIStatusError): + """422 - Request semantically invalid.""" + ... + +class RateLimitError(APIStatusError): + """429 - Rate limit exceeded.""" + ... + +class InternalServerError(APIStatusError): + """500+ - Server error.""" + ... + +class ServiceUnavailableError(APIStatusError): + """503 - Service temporarily unavailable.""" + ... + +class DeadlineExceededError(APIStatusError): + """504 - Request exceeded deadline.""" + ... + +class OverloadedError(APIStatusError): + """529 - Service overloaded.""" + ... +``` + +### Connection Errors + +```python { .api } +class APIConnectionError(APIError): + """Failed to connect to API.""" + ... + +class APITimeoutError(APIError): + """Request timed out.""" + ... + +class APIResponseValidationError(APIError): + """Response validation failed.""" + ... +``` + +## Error Response Format + +```python { .api } +class ErrorObject(BaseModel): + """Error object in API responses.""" + type: str + message: str + +class ErrorResponse(BaseModel): + """Error response wrapper.""" + type: Literal["error"] + error: ErrorObject +``` + +## Quick Examples + +### Basic Error Handling + +```python +from anthropic import APIError + +try: + message = client.messages.create(...) +except APIError as e: + print(f"Error: {e.message}") +``` + +### Handle Specific Errors + +```python +from anthropic import ( + RateLimitError, + AuthenticationError, + BadRequestError, +) + +try: + message = client.messages.create(...) +except RateLimitError as e: + retry_after = e.response.headers.get("retry-after") + print(f"Rate limited. Retry after {retry_after}s") +except AuthenticationError: + print("Invalid API key") +except BadRequestError as e: + print(f"Invalid request: {e.message}") +``` + +### Retry with Exponential Backoff + +```python +import time +from anthropic import RateLimitError, InternalServerError + +def create_message_with_retry(max_retries=3): + for attempt in range(max_retries): + try: + return client.messages.create(...) + except (RateLimitError, InternalServerError) as e: + if attempt == max_retries - 1: + raise + wait_time = 2 ** attempt + print(f"Retry {attempt + 1} after {wait_time}s") + time.sleep(wait_time) +``` + +### Extract Request ID + +```python +from anthropic import APIStatusError + +try: + message = client.messages.create(...) +except APIStatusError as e: + print(f"Request ID: {e.request_id}") + print(f"Status: {e.status_code}") +``` + +### Validate Response + +```python +from anthropic import APIResponseValidationError + +try: + message = client.messages.create(...) +except APIResponseValidationError as e: + print(f"Response validation failed: {e.message}") +``` + +### Handle All Errors + +```python +from anthropic import ( + APIError, + APIConnectionError, + APITimeoutError, + RateLimitError, +) + +try: + message = client.messages.create(...) +except APITimeoutError: + print("Request timed out") +except APIConnectionError: + print("Connection failed") +except RateLimitError: + print("Rate limit exceeded") +except APIError as e: + print(f"API error: {e.message}") +``` + +## Best Practices + +### Always Handle Exceptions + +```python +# Bad +message = client.messages.create(...) + +# Good +try: + message = client.messages.create(...) +except APIError as e: + # Handle error + ... +``` + +### Use Specific Exception Types + +```python +# Less precise +try: + message = client.messages.create(...) +except Exception: + ... + +# More precise +try: + message = client.messages.create(...) +except RateLimitError: + # Handle rate limit + ... +except APIError: + # Handle other API errors + ... +``` + +### Log Request Context + +```python +import logging + +logger = logging.getLogger(__name__) + +try: + message = client.messages.create(...) +except APIStatusError as e: + logger.error( + "API request failed", + extra={ + "request_id": e.request_id, + "status_code": e.status_code, + "error_message": e.message, + } + ) +``` + +## See Also + +- [Client Configuration](./client-config.md) - Configure retry and timeout behavior +- [Error Handling Guide](../guides/error-handling.md) - Advanced error handling patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md new file mode 100644 index 000000000..bfd82a606 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md @@ -0,0 +1,615 @@ +# Pagination Reference + +Auto-paginating iterators for list operations with manual control options. The SDK provides three pagination types: ID-based, token-based, and cursor-based. + +## Pagination Classes + +### Synchronous Pagination + +```python { .api } +class SyncPage(Generic[T]): + """ + Synchronous ID-based pagination. + + Provides: + - Automatic iteration over all items + - Manual page control + - Page metadata access + + Attributes: + data: Items in current page + """ + data: list[T] + + def __iter__(self) -> Iterator[T]: + """ + Iterate over all items across all pages automatically. + + Yields: + Individual items from current and subsequent pages + """ + ... + + def __getitem__(self, index: int) -> T: + """ + Get item by index in current page. + + Parameters: + index: Item index in current page + + Returns: + Item at index + + Raises: + IndexError: If index out of range + """ + ... + + def has_next_page(self) -> bool: + """ + Check if another page exists. + + Returns: + True if next page available + """ + ... + + def next_page_info(self) -> dict[str, Any]: + """ + Get information needed to fetch next page. + + Returns: + Dictionary with pagination parameters (after_id, limit, etc.) + """ + ... + + def get_next_page(self) -> SyncPage[T]: + """ + Fetch next page. + + Returns: + New SyncPage for next page + + Raises: + ValueError: If no next page exists + """ + ... + +class SyncTokenPage(Generic[T]): + """ + Synchronous token-based pagination. + + Similar to SyncPage but uses continuation tokens instead of IDs. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __iter__(self) -> Iterator[T]: + """Iterate over all items automatically.""" + ... + + def has_next_page(self) -> bool: + """Check if next page exists.""" + ... + + def next_page_info(self) -> dict[str, Any]: + """Get next page token.""" + ... + + def get_next_page(self) -> SyncTokenPage[T]: + """Fetch next page.""" + ... + +class SyncPageCursor(Generic[T]): + """ + Synchronous cursor-based pagination. + + Uses cursors for pagination instead of IDs or tokens. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __iter__(self) -> Iterator[T]: + """Iterate over all items automatically.""" + ... + + def has_next_page(self) -> bool: + """Check if next page exists.""" + ... + + def next_page_info(self) -> dict[str, Any]: + """Get next page cursor.""" + ... + + def get_next_page(self) -> SyncPageCursor[T]: + """Fetch next page.""" + ... +``` + +### Asynchronous Pagination + +```python { .api } +class AsyncPage(Generic[T]): + """ + Asynchronous ID-based pagination. + + Provides: + - Async automatic iteration over all items + - Async manual page control + - Page metadata access + + Attributes: + data: Items in current page + """ + data: list[T] + + def __aiter__(self) -> AsyncIterator[T]: + """ + Async iterate over all items across all pages. + + Yields: + Individual items from current and subsequent pages + """ + ... + + def __getitem__(self, index: int) -> T: + """Get item by index in current page.""" + ... + + async def has_next_page(self) -> bool: + """Check if another page exists.""" + ... + + async def next_page_info(self) -> dict[str, Any]: + """Get information needed to fetch next page.""" + ... + + async def get_next_page(self) -> AsyncPage[T]: + """Fetch next page.""" + ... + +class AsyncTokenPage(Generic[T]): + """ + Asynchronous token-based pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __aiter__(self) -> AsyncIterator[T]: ... + async def has_next_page(self) -> bool: ... + async def next_page_info(self) -> dict[str, Any]: ... + async def get_next_page(self) -> AsyncTokenPage[T]: ... + +class AsyncPageCursor(Generic[T]): + """ + Asynchronous cursor-based pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __aiter__(self) -> AsyncIterator[T]: ... + async def has_next_page(self) -> bool: ... + async def next_page_info(self) -> dict[str, Any]: ... + async def get_next_page(self) -> AsyncPageCursor[T]: ... +``` + +## Usage Examples + +### Auto-Pagination (Simple) + +```python +from anthropic import Anthropic + +client = Anthropic() + +# Automatically iterate over all batches +for batch in client.messages.batches.list(): + print(f"Batch {batch.id}: {batch.processing_status}") +``` + +### Auto-Pagination with Limit + +```python +# Get first 100 batches total (auto-fetching pages) +count = 0 +for batch in client.messages.batches.list(limit=20): + print(batch.id) + count += 1 + if count >= 100: + break +``` + +### Manual Pagination + +```python +# Get first page +page = client.messages.batches.list(limit=10) + +# Process first page +for batch in page.data: + print(f"Batch: {batch.id}") + +# Check if more pages +if page.has_next_page(): + print("More pages available") + + # Get next page info + next_info = page.next_page_info() + print(f"Next page params: {next_info}") + + # Fetch next page + next_page = page.get_next_page() + for batch in next_page.data: + print(f"Next page batch: {batch.id}") +``` + +### Iterate All Pages Manually + +```python +page = client.messages.batches.list(limit=10) + +while True: + # Process current page + for batch in page.data: + print(batch.id) + + # Check for next page + if not page.has_next_page(): + break + + # Fetch next page + page = page.get_next_page() +``` + +### Pagination with Before/After + +```python +# Get batches after specific ID +page = client.messages.batches.list( + after_id="batch_abc123", + limit=20 +) + +for batch in page: + print(batch.id) + +# Get batches before specific ID +page = client.messages.batches.list( + before_id="batch_xyz789", + limit=20 +) + +for batch in page: + print(batch.id) +``` + +### Access Current Page Data + +```python +page = client.messages.batches.list(limit=5) + +# Get items in current page +items = page.data +print(f"Current page has {len(items)} items") + +# Access by index +first_item = page[0] +print(f"First item: {first_item.id}") +``` + +### Async Auto-Pagination + +```python +import asyncio +from anthropic import AsyncAnthropic + +async def main(): + client = AsyncAnthropic() + + # Automatically iterate over all batches + async for batch in client.messages.batches.list(): + print(f"Batch {batch.id}: {batch.processing_status}") + +asyncio.run(main()) +``` + +### Async Manual Pagination + +```python +async def paginate_manually(): + client = AsyncAnthropic() + + # Get first page + page = await client.messages.batches.list(limit=10) + + # Process first page + for batch in page.data: + print(batch.id) + + # Check and fetch next page + if await page.has_next_page(): + next_page = await page.get_next_page() + for batch in next_page.data: + print(batch.id) + +asyncio.run(paginate_manually()) +``` + +### Async Iterate All Pages + +```python +async def iterate_all_pages(): + client = AsyncAnthropic() + + page = await client.messages.batches.list(limit=10) + + while True: + for batch in page.data: + print(batch.id) + + if not await page.has_next_page(): + break + + page = await page.get_next_page() + +asyncio.run(iterate_all_pages()) +``` + +### Pagination with Processing + +```python +# Collect all batches with filtering +completed_batches = [] + +for batch in client.messages.batches.list(): + if batch.processing_status == "ended": + completed_batches.append(batch) + +print(f"Found {len(completed_batches)} completed batches") +``` + +### Limit Total Items + +```python +# Get exactly 50 items total +items = [] +for batch in client.messages.batches.list(limit=20): + items.append(batch) + if len(items) >= 50: + break + +print(f"Collected {len(items)} items") +``` + +### Paginate with Error Handling + +```python +from anthropic import APIError + +try: + for batch in client.messages.batches.list(): + print(batch.id) +except APIError as e: + print(f"Pagination error: {e}") +``` + +### Concurrent Page Fetching + +```python +import asyncio + +async def fetch_multiple_pages(): + client = AsyncAnthropic() + + # Fetch first page + page1 = await client.messages.batches.list(limit=10) + + # Fetch multiple subsequent pages concurrently + if await page1.has_next_page(): + next_info = await page1.next_page_info() + + pages = await asyncio.gather( + page1.get_next_page(), + client.messages.batches.list(**next_info, limit=10), + ) + + for page in pages: + for batch in page.data: + print(batch.id) + +asyncio.run(fetch_multiple_pages()) +``` + +### List Models with Pagination + +```python +# Auto-paginate through all models +for model in client.models.list(): + print(f"{model.id}: {model.display_name}") +``` + +### Count Items with Pagination + +```python +# Count total items +total = 0 +for batch in client.messages.batches.list(): + total += 1 + +print(f"Total batches: {total}") +``` + +### Pagination Performance + +```python +import time + +start = time.time() + +# Efficient pagination with larger page size +count = 0 +for batch in client.messages.batches.list(limit=100): + count += 1 + +elapsed = time.time() - start +print(f"Processed {count} items in {elapsed:.2f} seconds") +``` + +### Page Metadata + +```python +page = client.messages.batches.list(limit=10) + +print(f"Items in page: {len(page.data)}") +print(f"Has next: {page.has_next_page()}") + +if page.has_next_page(): + next_info = page.next_page_info() + print(f"Next page info: {next_info}") +``` + +### Reverse Pagination + +```python +# Get most recent items first (default) +for batch in client.messages.batches.list(limit=10): + print(f"Recent: {batch.id} - {batch.created_at}") + +# Get older items using before_id +oldest_on_page = None +for batch in page.data: + oldest_on_page = batch.id + +if oldest_on_page: + older_page = client.messages.batches.list(before_id=oldest_on_page, limit=10) + for batch in older_page: + print(f"Older: {batch.id}") +``` + +### Collect All Items + +```python +# Collect all items into list +all_batches = [] +for batch in client.messages.batches.list(): + all_batches.append(batch) + +print(f"Total batches: {len(all_batches)}") +``` + +### Batch Processing Pages + +```python +def process_batch(batches): + """Process a batch of items.""" + for batch in batches: + print(f"Processing {batch.id}") + +page = client.messages.batches.list(limit=50) +while True: + process_batch(page.data) + + if not page.has_next_page(): + break + + page = page.get_next_page() +``` + +### Custom Page Size + +```python +# Use small pages for frequent updates +for batch in client.messages.batches.list(limit=5): + print(batch.id) + +# Use large pages for bulk processing +for batch in client.messages.batches.list(limit=100): + print(batch.id) +``` + +## JSONL Stream Decoders + +For batch results that return JSONL streams: + +```python { .api } +from typing import Generic, TypeVar, Iterator, AsyncIterator + +T = TypeVar('T') + +class JSONLDecoder(Generic[T]): + """ + Synchronous JSONL stream decoder. + + Decodes newline-delimited JSON objects from streaming responses, + commonly used for batch result streaming. + + Yields: + Decoded objects of type T, one per JSONL line + """ + + def __iter__(self) -> Iterator[T]: + """Iterate over decoded JSONL objects.""" + ... + +class AsyncJSONLDecoder(Generic[T]): + """ + Asynchronous JSONL stream decoder. + + Async version for decoding JSONL streams. + + Yields: + Decoded objects of type T, one per JSONL line + """ + + def __aiter__(self) -> AsyncIterator[T]: + """Async iterate over decoded JSONL objects.""" + ... +``` + +### JSONL Decoder Usage + +```python +# Stream batch results (returns JSONL decoder) +results = client.messages.batches.results("batch_abc123") + +# Iterate over individual results +for result in results: + if result.result.type == "succeeded": + print(f"Message: {result.result.message.content[0].text}") + elif result.result.type == "errored": + print(f"Error: {result.result.error.message}") +``` + +### Async JSONL Decoding + +```python +import asyncio + +async def process_batch_results(): + client = AsyncAnthropic() + + # Get async JSONL decoder + results = await client.messages.batches.results("batch_abc123") + + # Async iterate + async for result in results: + print(f"Custom ID: {result.custom_id}") + # Process result + +asyncio.run(process_batch_results()) +``` + +## See Also + +- [Messages API](../api/messages.md) - Message pagination +- [Batches API](../api/batches.md) - Batch pagination and JSONL results +- [Models API](../api/models.md) - Model pagination +- [Type System](./types.md) - Pagination type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md new file mode 100644 index 000000000..f1848d563 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md @@ -0,0 +1,1413 @@ +# Type System Reference + +Comprehensive type definitions for all request parameters and response objects using Pydantic models and TypedDict. All types are fully typed for static analysis and IDE support. + +## Core Message Types + +### Message + +```python { .api } +class Message(BaseModel): + """ + Complete message response from Claude. + + Attributes: + id: Unique message identifier (starts with "msg_") + type: Always "message" + role: Always "assistant" + content: List of content blocks (text, tool_use, etc.) + model: Model identifier used for generation + stop_reason: Why generation stopped + stop_sequence: Stop sequence that triggered completion (if any) + usage: Token usage statistics + """ + id: str + type: Literal["message"] + role: Literal["assistant"] + content: list[ContentBlock] + model: str + stop_reason: StopReason | None + stop_sequence: str | None + usage: Usage +``` + +### MessageParam + +```python { .api } +class MessageParam(TypedDict): + """ + User or assistant message in conversation. + + Fields: + role: "user" or "assistant" + content: String or list of content blocks + """ + role: Literal["user", "assistant"] + content: str | list[ContentBlockParam] +``` + +### ContentBlock (Response) + +```python { .api } +ContentBlock = Union[TextBlock, ToolUseBlock] + +class TextBlock(BaseModel): + """ + Text content in assistant response. + + Attributes: + type: Always "text" + text: The text content + """ + type: Literal["text"] + text: str + +class ToolUseBlock(BaseModel): + """ + Tool invocation in assistant response. + + Attributes: + type: Always "tool_use" + id: Unique tool call identifier + name: Tool name + input: Tool input parameters as dict + """ + type: Literal["tool_use"] + id: str + name: str + input: dict[str, Any] +``` + +### ContentBlockParam (Request) + +```python { .api } +ContentBlockParam = Union[ + TextBlockParam, + ImageBlockParam, + DocumentBlockParam, + ToolUseBlockParam, + ToolResultBlockParam, +] + +class TextBlockParam(TypedDict): + """ + Text content in message. + + Fields: + type: Always "text" + text: The text content + cache_control: Optional cache control + """ + type: Literal["text"] + text: str + cache_control: NotRequired[CacheControlEphemeral] + +class ImageBlockParam(TypedDict): + """ + Image content in message. + + Fields: + type: Always "image" + source: Image source (base64 or URL) + cache_control: Optional cache control + """ + type: Literal["image"] + source: Base64ImageSource | URLImageSource + cache_control: NotRequired[CacheControlEphemeral] + +class DocumentBlockParam(TypedDict): + """ + Document content (PDF, text) in message. + + Fields: + type: Always "document" + source: Document source (base64 or URL) + cache_control: Optional cache control + """ + type: Literal["document"] + source: Base64PDFSource | URLPDFSource | PlainTextSource + cache_control: NotRequired[CacheControlEphemeral] + +class ToolUseBlockParam(TypedDict): + """ + Tool invocation in assistant message (for conversation history). + + Fields: + type: Always "tool_use" + id: Tool call identifier + name: Tool name + input: Tool input parameters + cache_control: Optional cache control + """ + type: Literal["tool_use"] + id: str + name: str + input: dict[str, Any] + cache_control: NotRequired[CacheControlEphemeral] + +class ToolResultBlockParam(TypedDict): + """ + Tool result in user message. + + Fields: + type: Always "tool_result" + tool_use_id: ID of tool invocation this is result for + content: String or list of content blocks + is_error: Whether result represents an error + cache_control: Optional cache control + """ + type: Literal["tool_result"] + tool_use_id: str + content: NotRequired[str | list[TextBlockParam | ImageBlockParam]] + is_error: NotRequired[bool] + cache_control: NotRequired[CacheControlEphemeral] +``` + +## Content Source Types + +### Image Sources + +```python { .api } +class Base64ImageSource(TypedDict): + """ + Base64-encoded image. + + Fields: + type: Always "base64" + media_type: MIME type (jpeg, png, gif, webp) + data: Base64-encoded image data (without data URL prefix) + """ + type: Literal["base64"] + media_type: Literal["image/jpeg", "image/png", "image/gif", "image/webp"] + data: str + +class URLImageSource(TypedDict): + """ + Image from URL. + + Fields: + type: Always "url" + url: Image URL (must be publicly accessible) + """ + type: Literal["url"] + url: str +``` + +### Document Sources + +```python { .api } +class Base64PDFSource(TypedDict): + """ + Base64-encoded PDF document. + + Fields: + type: Always "base64" + media_type: Always "application/pdf" + data: Base64-encoded PDF data + """ + type: Literal["base64"] + media_type: Literal["application/pdf"] + data: str + +class URLPDFSource(TypedDict): + """ + PDF document from URL. + + Fields: + type: Always "url" + media_type: Always "application/pdf" + url: PDF URL + """ + type: Literal["url"] + media_type: Literal["application/pdf"] + url: str + +class PlainTextSource(TypedDict): + """ + Plain text document. + + Fields: + type: Always "text" + media_type: Always "text/plain" + data: Plain text content + """ + type: Literal["text"] + media_type: Literal["text/plain"] + data: str +``` + +## Tool Types + +### Tool Definition + +```python { .api } +class Tool(BaseModel): + """ + Tool definition in response. + + Attributes: + name: Tool name + description: Tool description + input_schema: JSON Schema for parameters + """ + name: str + description: str + input_schema: dict[str, Any] + +class ToolParam(TypedDict): + """ + Tool definition in request. + + Fields: + name: Tool name (alphanumeric + underscores) + description: What the tool does + input_schema: JSON Schema for tool parameters + cache_control: Optional cache control + """ + name: str + description: str + input_schema: dict[str, Any] + cache_control: NotRequired[CacheControlEphemeral] +``` + +### Tool Choice + +```python { .api } +ToolChoice = Union[ + ToolChoiceAuto, + ToolChoiceAny, + ToolChoiceNone, + ToolChoiceTool, +] + +class ToolChoiceAuto(TypedDict): + """ + Let Claude decide whether to use tools (default). + + Fields: + type: Always "auto" + disable_parallel_tool_use: Disable parallel tool calls + """ + type: Literal["auto"] + disable_parallel_tool_use: NotRequired[bool] + +class ToolChoiceAny(TypedDict): + """ + Force Claude to use at least one tool. + + Fields: + type: Always "any" + disable_parallel_tool_use: Disable parallel tool calls + """ + type: Literal["any"] + disable_parallel_tool_use: NotRequired[bool] + +class ToolChoiceNone(TypedDict): + """ + Disable all tool use. + + Fields: + type: Always "none" + """ + type: Literal["none"] + +class ToolChoiceTool(TypedDict): + """ + Force Claude to use specific tool. + + Fields: + type: Always "tool" + name: Tool name to use + disable_parallel_tool_use: Disable parallel tool calls + """ + type: Literal["tool"] + name: str + disable_parallel_tool_use: NotRequired[bool] +``` + +## Configuration Types + +### Usage + +```python { .api } +class Usage(BaseModel): + """ + Token usage statistics. + + Attributes: + input_tokens: Number of input tokens + output_tokens: Number of output tokens + cache_creation_input_tokens: Tokens used to create cache (if using prompt caching) + cache_read_input_tokens: Tokens read from cache (if using prompt caching) + """ + input_tokens: int + output_tokens: int + cache_creation_input_tokens: int | None = None + cache_read_input_tokens: int | None = None +``` + +### Metadata + +```python { .api } +class Metadata(BaseModel): + """ + Response metadata. + + Attributes: + user_id: End-user identifier (if provided) + """ + user_id: str | None = None + +class MetadataParam(TypedDict, total=False): + """ + Request metadata for tracking and compliance. + + Fields: + user_id: End-user identifier for tracking and abuse prevention + """ + user_id: str +``` + +### Cache Control + +```python { .api } +class CacheControlEphemeral(TypedDict): + """ + Ephemeral cache control for prompt caching. + + Fields: + type: Always "ephemeral" + """ + type: Literal["ephemeral"] +``` + +### Thinking Configuration + +```python { .api } +ThinkingConfigParam = Union[ThinkingConfigEnabled, ThinkingConfigDisabled] + +class ThinkingConfigEnabled(TypedDict): + """ + Enable extended thinking for enhanced reasoning. + + Fields: + type: Always "enabled" + """ + type: Literal["enabled"] + +class ThinkingConfigDisabled(TypedDict): + """ + Disable extended thinking. + + Fields: + type: Always "disabled" + """ + type: Literal["disabled"] +``` + +### Stop Reason + +```python { .api } +StopReason = Literal[ + "end_turn", # Natural completion + "max_tokens", # Hit max_tokens limit + "stop_sequence", # Hit custom stop sequence + "tool_use", # Model wants to use a tool +] +``` + +## Token Counting Types + +```python { .api } +class MessageTokensCount(BaseModel): + """ + Token count response. + + Attributes: + input_tokens: Number of input tokens + """ + input_tokens: int +``` + +## Batch Types + +### Batch + +```python { .api } +class MessageBatch(BaseModel): + """ + Batch metadata and status. + + Attributes: + id: Unique batch identifier (starts with "msgbatch_") + type: Always "message_batch" + processing_status: Current processing status + request_counts: Counts by result type + ended_at: When batch completed (ISO 8601) + created_at: When batch was created (ISO 8601) + expires_at: When batch results will expire (ISO 8601) + cancel_initiated_at: When cancellation was initiated (ISO 8601) + results_url: URL to download results JSONL + """ + id: str + type: Literal["message_batch"] + processing_status: Literal["in_progress", "canceling", "ended"] + request_counts: MessageBatchRequestCounts + ended_at: str | None + created_at: str + expires_at: str + cancel_initiated_at: str | None + results_url: str | None + +class MessageBatchRequestCounts(BaseModel): + """ + Request count statistics in batch. + + Attributes: + processing: Requests currently processing + succeeded: Successful requests + errored: Failed requests + canceled: Canceled requests + expired: Expired requests + """ + processing: int + succeeded: int + errored: int + canceled: int + expired: int + +class DeletedMessageBatch(BaseModel): + """ + Deleted batch confirmation. + + Attributes: + id: Batch identifier that was deleted + type: Always "message_batch_deleted" + """ + id: str + type: Literal["message_batch_deleted"] +``` + +### Batch Request/Response + +```python { .api } +class MessageBatchIndividualRequest(TypedDict): + """ + Individual request in batch. + + Fields: + custom_id: Client-provided unique identifier + params: Message creation parameters + """ + custom_id: str + params: MessageCreateParams + +class MessageBatchIndividualResponse(BaseModel): + """ + Individual response in batch results. + + Attributes: + custom_id: Client-provided identifier from request + result: Result object (success/error/canceled/expired) + """ + custom_id: str + result: MessageBatchResult + +MessageBatchResult = Union[ + MessageBatchSucceededResult, + MessageBatchErroredResult, + MessageBatchCanceledResult, + MessageBatchExpiredResult, +] + +class MessageBatchSucceededResult(BaseModel): + """ + Successful batch result. + + Attributes: + type: Always "succeeded" + message: The message response + """ + type: Literal["succeeded"] + message: Message + +class MessageBatchErroredResult(BaseModel): + """ + Failed batch result. + + Attributes: + type: Always "errored" + error: Error details + """ + type: Literal["errored"] + error: ErrorObject + +class MessageBatchCanceledResult(BaseModel): + """ + Canceled batch result. + + Attributes: + type: Always "canceled" + """ + type: Literal["canceled"] + +class MessageBatchExpiredResult(BaseModel): + """ + Expired batch result. + + Attributes: + type: Always "expired" + """ + type: Literal["expired"] +``` + +## Model Types + +```python { .api } +class ModelInfo(BaseModel): + """ + Model information and capabilities. + + Attributes: + id: Model identifier + type: Always "model" + display_name: Human-readable model name + created_at: When model was created (ISO 8601) + """ + id: str + type: Literal["model"] + display_name: str + created_at: str + +Model = Literal[ + "claude-opus-4-5-20250929", + "claude-sonnet-4-5-20250929", + "claude-3-5-sonnet-20241022", + "claude-3-5-sonnet-20240620", + "claude-3-5-haiku-20241022", + "claude-3-opus-20240229", + "claude-3-sonnet-20240229", + "claude-3-haiku-20240307", + # Additional models... +] +``` + +## Error Types + +```python { .api } +class ErrorObject(BaseModel): + """ + Error object in API responses. + + Attributes: + type: Error type identifier + message: Human-readable error message + """ + type: str + message: str + +class ErrorResponse(BaseModel): + """ + Error response wrapper. + + Attributes: + type: Always "error" + error: Error details + """ + type: Literal["error"] + error: APIErrorObject + +class APIErrorObject(BaseModel): + """ + API error object. + + Attributes: + type: Error type + message: Error message + """ + type: str + message: str +``` + +## Stream Event Types + +### Message Stream Events + +```python { .api } +MessageStreamEvent = Union[ + MessageStartEvent, + MessageDeltaEvent, + MessageStopEvent, + ContentBlockStartEvent, + ContentBlockDeltaEvent, + ContentBlockStopEvent, +] + +class MessageStartEvent(BaseModel): + """ + Stream started with initial message. + + Attributes: + type: Always "message_start" + message: Initial message with empty content + """ + type: Literal["message_start"] + message: Message + +class MessageDeltaEvent(BaseModel): + """ + Message metadata changed. + + Attributes: + type: Always "message_delta" + delta: Changed fields + usage: Token usage update + """ + type: Literal["message_delta"] + delta: MessageDelta + usage: MessageDeltaUsage + +class MessageDelta(BaseModel): + """ + Message field changes. + + Attributes: + stop_reason: Updated stop reason + stop_sequence: Stop sequence that triggered + """ + stop_reason: StopReason | None + stop_sequence: str | None + +class MessageDeltaUsage(BaseModel): + """ + Token usage in stream delta. + + Attributes: + output_tokens: Output tokens generated so far + """ + output_tokens: int + +class MessageStopEvent(BaseModel): + """ + Stream completed. + + Attributes: + type: Always "message_stop" + """ + type: Literal["message_stop"] +``` + +### Content Block Stream Events + +```python { .api } +class ContentBlockStartEvent(BaseModel): + """ + New content block started. + + Attributes: + type: Always "content_block_start" + index: Content block index + content_block: Initial content block + """ + type: Literal["content_block_start"] + index: int + content_block: ContentBlock + +class ContentBlockDeltaEvent(BaseModel): + """ + Content block received delta. + + Attributes: + type: Always "content_block_delta" + index: Content block index + delta: Delta content + """ + type: Literal["content_block_delta"] + index: int + delta: ContentBlockDelta + +ContentBlockDelta = Union[TextDelta, InputJSONDelta] + +class TextDelta(BaseModel): + """ + Text content delta. + + Attributes: + type: Always "text_delta" + text: Incremental text + """ + type: Literal["text_delta"] + text: str + +class InputJSONDelta(BaseModel): + """ + Tool input JSON delta. + + Attributes: + type: Always "input_json_delta" + partial_json: Incremental JSON string + """ + type: Literal["input_json_delta"] + partial_json: str + +class ContentBlockStopEvent(BaseModel): + """ + Content block completed. + + Attributes: + type: Always "content_block_stop" + index: Content block index + """ + type: Literal["content_block_stop"] + index: int +``` + +### Raw Stream Events + +```python { .api } +RawMessageStreamEvent = Union[ + RawMessageStartEvent, + RawMessageDeltaEvent, + RawMessageStopEvent, + RawContentBlockStartEvent, + RawContentBlockDeltaEvent, + RawContentBlockStopEvent, +] + +class RawMessageStartEvent(BaseModel): + """ + Raw message start event from SSE. + + Attributes: + type: Always "message_start" + message: Initial message + """ + type: Literal["message_start"] + message: Message + +class RawMessageDeltaEvent(BaseModel): + """ + Raw message delta event from SSE. + + Attributes: + type: Always "message_delta" + delta: Changed fields as dict + usage: Token usage update + """ + type: Literal["message_delta"] + delta: dict[str, Any] + usage: MessageDeltaUsage + +class RawMessageStopEvent(BaseModel): + """ + Raw message stop event from SSE. + + Attributes: + type: Always "message_stop" + """ + type: Literal["message_stop"] + +class RawContentBlockStartEvent(BaseModel): + """ + Raw content block start from SSE. + + Attributes: + type: Always "content_block_start" + index: Content block index + content_block: Initial content block as dict + """ + type: Literal["content_block_start"] + index: int + content_block: dict[str, Any] + +class RawContentBlockDeltaEvent(BaseModel): + """ + Raw content block delta from SSE. + + Attributes: + type: Always "content_block_delta" + index: Content block index + delta: Delta content as dict + """ + type: Literal["content_block_delta"] + index: int + delta: dict[str, Any] + +class RawContentBlockStopEvent(BaseModel): + """ + Raw content block stop from SSE. + + Attributes: + type: Always "content_block_stop" + index: Content block index + """ + type: Literal["content_block_stop"] + index: int +``` + +## Pagination Types + +### Synchronous Pagination + +```python { .api } +class SyncPage(Generic[T]): + """ + Synchronous ID-based pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __iter__(self) -> Iterator[T]: + """Iterate over all items across pages automatically.""" + ... + + def __getitem__(self, index: int) -> T: + """Get item by index in current page.""" + ... + + def has_next_page(self) -> bool: + """Check if another page exists.""" + ... + + def next_page_info(self) -> dict[str, Any]: + """Get pagination parameters for next page.""" + ... + + def get_next_page(self) -> SyncPage[T]: + """Fetch next page.""" + ... + +class SyncTokenPage(Generic[T]): + """ + Synchronous token-based pagination. + + Uses continuation tokens instead of IDs. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __iter__(self) -> Iterator[T]: ... + def has_next_page(self) -> bool: ... + def next_page_info(self) -> dict[str, Any]: ... + def get_next_page(self) -> SyncTokenPage[T]: ... + +class SyncPageCursor(Generic[T]): + """ + Synchronous cursor-based pagination. + + Uses cursors for pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __iter__(self) -> Iterator[T]: ... + def has_next_page(self) -> bool: ... + def next_page_info(self) -> dict[str, Any]: ... + def get_next_page(self) -> SyncPageCursor[T]: ... +``` + +### Asynchronous Pagination + +```python { .api } +class AsyncPage(Generic[T]): + """ + Asynchronous ID-based pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __aiter__(self) -> AsyncIterator[T]: + """Async iterate over all items across pages.""" + ... + + def __getitem__(self, index: int) -> T: + """Get item by index in current page.""" + ... + + async def has_next_page(self) -> bool: + """Check if another page exists.""" + ... + + async def next_page_info(self) -> dict[str, Any]: + """Get pagination parameters for next page.""" + ... + + async def get_next_page(self) -> AsyncPage[T]: + """Fetch next page.""" + ... + +class AsyncTokenPage(Generic[T]): + """ + Asynchronous token-based pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __aiter__(self) -> AsyncIterator[T]: ... + async def has_next_page(self) -> bool: ... + async def next_page_info(self) -> dict[str, Any]: ... + async def get_next_page(self) -> AsyncTokenPage[T]: ... + +class AsyncPageCursor(Generic[T]): + """ + Asynchronous cursor-based pagination. + + Attributes: + data: Items in current page + """ + data: list[T] + + def __aiter__(self) -> AsyncIterator[T]: ... + async def has_next_page(self) -> bool: ... + async def next_page_info(self) -> dict[str, Any]: ... + async def get_next_page(self) -> AsyncPageCursor[T]: ... +``` + +## Type Helpers + +```python { .api } +class NOT_GIVEN: + """ + Sentinel value for omitted optional parameters. + + Used to distinguish between explicitly passing None vs not passing a parameter. + This allows the SDK to differentiate between: + - Parameter not provided (NOT_GIVEN) + - Parameter explicitly set to None (None) + """ + ... + +NotGiven = type[NOT_GIVEN] + +def not_given() -> NOT_GIVEN: + """Return NOT_GIVEN sentinel.""" + ... + +class Omit: + """ + Type for omitted fields. + + Used internally for partial updates where some fields should be omitted. + """ + ... + +def omit() -> Omit: + """Return Omit sentinel.""" + ... + +NoneType = type[None] +``` + +## Request Parameter Types + +```python { .api } +class MessageCreateParams(TypedDict): + """ + Complete parameters for message creation. + + Fields: + model: Model identifier (required) + messages: Conversation messages (required) + max_tokens: Maximum tokens to generate (required) + system: System prompt (optional) + metadata: Request metadata (optional) + stop_sequences: Stop sequences (optional) + stream: Enable streaming (optional) + temperature: Sampling temperature 0.0-1.0 (optional) + top_p: Nucleus sampling (optional) + top_k: Top-k sampling (optional) + tools: Available tools (optional) + tool_choice: Tool selection control (optional) + service_tier: Service tier selection (optional) + thinking: Extended thinking configuration (optional) + """ + model: str + messages: list[MessageParam] + max_tokens: int + system: NotRequired[str | list[TextBlockParam]] + metadata: NotRequired[MetadataParam] + stop_sequences: NotRequired[list[str]] + stream: NotRequired[bool] + temperature: NotRequired[float] + top_p: NotRequired[float] + top_k: NotRequired[int] + tools: NotRequired[list[ToolParam]] + tool_choice: NotRequired[ToolChoice] + service_tier: NotRequired[Literal["auto", "standard_only"]] + thinking: NotRequired[ThinkingConfigParam] + +class RequestOptions(TypedDict, total=False): + """ + Per-request options. + + Fields: + headers: Custom headers + max_retries: Maximum retry attempts + timeout: Request timeout + query: Custom query parameters + """ + headers: dict[str, str] + max_retries: int + timeout: float | httpx.Timeout + query: dict[str, object] +``` + +## Response Wrapper Types + +```python { .api } +class APIResponse(Generic[T]): + """ + HTTP response wrapper with parsed data. + + Attributes: + data: Parsed response data + headers: Response headers + status_code: HTTP status code + request: Original request + """ + data: T + headers: dict[str, str] + status_code: int + request: httpx.Request + + def parse(self) -> T: + """ + Get parsed response data. + + Returns: + Parsed response object of type T + """ + ... + +class AsyncAPIResponse(Generic[T]): + """ + Async HTTP response wrapper. + + Attributes: + data: Parsed response data + headers: Response headers + status_code: HTTP status code + request: Original request + """ + data: T + headers: dict[str, str] + status_code: int + request: httpx.Request + + async def parse(self) -> T: + """ + Get parsed response data asynchronously. + + Returns: + Parsed response object of type T + """ + ... +``` + +## Base Model + +```python { .api } +class BaseModel(pydantic.BaseModel): + """ + Base Pydantic model for all response types. + + Provides: + - JSON serialization/deserialization + - Type validation + - Field access + - Model copying + - Schema generation + """ + def model_dump( + self, + *, + mode: Literal["json", "python"] = "python", + include: set[str] | None = None, + exclude: set[str] | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> dict[str, Any]: + """ + Convert model to dictionary. + + Parameters: + mode: Serialization mode ("json" or "python") + include: Fields to include + exclude: Fields to exclude + by_alias: Use field aliases + exclude_unset: Exclude fields that weren't set + exclude_defaults: Exclude fields with default values + exclude_none: Exclude fields with None values + + Returns: + Dictionary representation of model + """ + ... + + def model_dump_json( + self, + *, + indent: int | None = None, + include: set[str] | None = None, + exclude: set[str] | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> str: + """ + Convert model to JSON string. + + Parameters: + indent: JSON indentation + include: Fields to include + exclude: Fields to exclude + by_alias: Use field aliases + exclude_unset: Exclude fields that weren't set + exclude_defaults: Exclude fields with default values + exclude_none: Exclude fields with None values + + Returns: + JSON string representation + """ + ... + + @classmethod + def model_validate(cls, obj: Any) -> Self: + """ + Validate and parse object into model. + + Parameters: + obj: Object to validate (dict, model instance, etc.) + + Returns: + Validated model instance + + Raises: + ValidationError: If validation fails + """ + ... + + @classmethod + def model_validate_json(cls, json_data: str | bytes) -> Self: + """ + Validate and parse JSON into model. + + Parameters: + json_data: JSON string or bytes + + Returns: + Validated model instance + + Raises: + ValidationError: If validation fails + """ + ... + + def model_copy( + self, + *, + update: dict[str, Any] | None = None, + deep: bool = False, + ) -> Self: + """ + Create copy of model with optional updates. + + Parameters: + update: Dictionary of fields to update + deep: Whether to make deep copy + + Returns: + New model instance + """ + ... + + @classmethod + def model_json_schema( + cls, + *, + by_alias: bool = True, + ref_template: str = "#/$defs/{model}", + ) -> dict[str, Any]: + """ + Generate JSON Schema for model. + + Parameters: + by_alias: Use field aliases in schema + ref_template: Template for $ref values + + Returns: + JSON Schema dictionary + """ + ... +``` + +## SDK Infrastructure Types + +### HTTP and Client Types + +```python { .api } +from typing import Union +import httpx + +Timeout = Union[float, httpx.Timeout, None] +""" +Timeout specification for HTTP requests. + +Can be: +- float: Total timeout in seconds +- httpx.Timeout: Granular timeout with connect/read/write/pool settings +- None: No timeout (not recommended) +""" + +Transport = Union[httpx.HTTPTransport, httpx.AsyncHTTPTransport] +"""HTTP transport for custom connection pooling and proxying.""" + +ProxiesTypes = Union[str, httpx.Proxy, dict[str, Union[str, httpx.Proxy]]] +""" +Proxy configuration types. + +Can be: +- str: Proxy URL +- httpx.Proxy: Configured proxy object +- dict: Mapping of protocols to proxy URLs +""" + +FileTypes = tuple[str, bytes, str] +""" +File tuple for upload operations. + +Format: (filename, content, mime_type) + +Example: + ("document.pdf", pdf_bytes, "application/pdf") +""" +``` + +### Sentinel Types + +```python { .api } +class NotGivenType: + """Singleton type for NOT_GIVEN sentinel value.""" + ... + +NotGiven = NotGivenType +""" +Sentinel value indicating a parameter was not provided. + +Used to distinguish between None (explicitly passed) and +parameter not passed at all. + +Example: + def create(*, param: str | None | NotGiven = NOT_GIVEN): + if param is NOT_GIVEN: + # Parameter not provided + elif param is None: + # Parameter explicitly set to None +""" + +NOT_GIVEN: NotGiven +"""Singleton instance of NotGiven sentinel.""" +``` + +### Response Wrapper Types + +```python { .api } +from typing import Generic, TypeVar + +T = TypeVar('T') + +class APIResponse(Generic[T]): + """ + Wrapper providing access to raw HTTP response and parsed data. + + Attributes: + http_response: Raw httpx.Response object + data: Cached parsed response data + """ + http_response: httpx.Response + + def parse(self) -> T: + """ + Parse and return typed response data. + + Returns: + Parsed response object of type T + """ + ... + +class AsyncAPIResponse(Generic[T]): + """ + Async version of APIResponse. + + Attributes: + http_response: Raw httpx.Response object + data: Cached parsed response data + """ + http_response: httpx.Response + + async def parse(self) -> T: + """ + Async parse and return typed response data. + + Returns: + Parsed response object of type T + """ + ... +``` + +### Base Model + +```python { .api } +from pydantic import BaseModel as PydanticBaseModel + +class BaseModel(PydanticBaseModel): + """ + Base Pydantic model with SDK-specific configuration. + + All response types inherit from this class, providing: + - JSON serialization via .model_dump() and .model_dump_json() + - Type validation + - Field aliases and exclusions + """ + + def model_dump(self, **kwargs) -> dict[str, Any]: + """Serialize model to dictionary.""" + ... + + def model_dump_json(self, **kwargs) -> str: + """Serialize model to JSON string.""" + ... + + @classmethod + def model_validate(cls, obj: Any) -> Self: + """Validate and construct model from dict.""" + ... + + @classmethod + def model_json_schema(cls, **kwargs) -> dict[str, Any]: + """Generate JSON Schema for model.""" + ... +``` + +## See Also + +- [Messages API](../api/messages.md) - Core message types in use +- [Streaming API](../api/streaming.md) - Stream event types in use +- [Tool Use API](../api/tools.md) - Tool types in use +- [Batches API](../api/batches.md) - Batch types in use +- [Error Handling](./errors.md) - Error types and exception hierarchy +- [Client Configuration](./client-config.md) - Usage of Timeout and Transport types +- [Utilities](./utilities.md) - Usage of FileTypes diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md new file mode 100644 index 000000000..dde1fc69d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md @@ -0,0 +1,169 @@ +# Utilities Reference + +Helper functions for file handling, schema transformation, and HTTP client customization. + +## File Utilities + +```python { .api } +def file_from_path(path: str | Path) -> FileTypes: + """ + Load single file from filesystem path. + + Parameters: + path: File path (string or Path object) + + Returns: + File-like object for API requests (tuple of filename, content, mime-type) + + Raises: + FileNotFoundError: If path does not exist + IsADirectoryError: If path is a directory + """ + ... + +def files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: + """ + Load all files from a directory recursively. + + Recursively walks through directory and loads all files, returning them + as a list suitable for batch file upload operations. + + Parameters: + directory: Directory path (string or PathLike object) + + Returns: + List of file objects, each as (filename, content, mime-type) tuple + + Raises: + FileNotFoundError: If directory does not exist + NotADirectoryError: If path is not a directory + """ + ... + +async def async_files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: + """ + Async version of files_from_dir(). + + Asynchronously loads all files from directory recursively. Useful for + large directories or when loading many files in async context. + + Parameters: + directory: Directory path (string or PathLike object) + + Returns: + List of file objects, each as (filename, content, mime-type) tuple + + Raises: + FileNotFoundError: If directory does not exist + NotADirectoryError: If path is not a directory + """ + ... +``` + +## Schema Transformation + +```python { .api } +def transform_schema(schema: dict[str, Any]) -> dict[str, Any]: + """ + Transform JSON schema for structured outputs. + + Parameters: + schema: JSON Schema dictionary + + Returns: + Transformed schema for API + """ + ... +``` + +## HTTP Client Factories + +```python { .api } +class DefaultHttpxClient: + """Default synchronous HTTP client.""" + def __init__( + self, + *, + proxy: str | httpx.Proxy | None = None, + transport: httpx.HTTPTransport | None = None, + **kwargs + ): ... + +class DefaultAsyncHttpxClient: + """Default asynchronous HTTP client.""" + def __init__( + self, + *, + proxy: str | httpx.Proxy | None = None, + transport: httpx.AsyncHTTPTransport | None = None, + **kwargs + ): ... + +class DefaultAioHttpClient: + """Alternative async HTTP client using aiohttp.""" + def __init__(self, **kwargs): ... +``` + +## Quick Examples + +### Load Single File + +```python +from anthropic._utils import file_from_path + +file = file_from_path("document.pdf") +uploaded = client.beta.files.upload(file, purpose="batch") +``` + +### Load Multiple Files from Directory + +```python +from anthropic._utils import files_from_dir + +# Load all files from a directory +files = files_from_dir("./documents") +print(f"Loaded {len(files)} files") + +# Upload all files for batch processing +for file in files: + uploaded = client.beta.files.upload(file, purpose="batch") + print(f"Uploaded: {uploaded.id}") +``` + +### Async Load Files from Directory + +```python +import asyncio +from anthropic._utils import async_files_from_dir + +async def upload_directory(): + client = AsyncAnthropic() + + # Asynchronously load all files + files = await async_files_from_dir("./documents") + + # Upload concurrently + tasks = [client.beta.files.upload(f, purpose="batch") for f in files] + results = await asyncio.gather(*tasks) + + print(f"Uploaded {len(results)} files") + +asyncio.run(upload_directory()) +``` + +### Transform Schema + +```python +from pydantic import BaseModel +from anthropic.lib._parse._transform import transform_schema + +class Response(BaseModel): + answer: str + confidence: float + +schema = transform_schema(Response.model_json_schema()) +``` + +## See Also + +- [Client Configuration](./client-config.md) - Client initialization diff --git a/.tessl/tiles/tessl/pypi-anthropic/tile.json b/.tessl/tiles/tessl/pypi-anthropic/tile.json new file mode 100644 index 000000000..e86f85ede --- /dev/null +++ b/.tessl/tiles/tessl/pypi-anthropic/tile.json @@ -0,0 +1,8 @@ +{ + "name": "tessl/pypi-anthropic", + "version": "0.75.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/anthropic@0.75.0", + "summary": "The official Python library for the anthropic API", + "private": false +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/configuration.md b/.tessl/tiles/tessl/pypi-dill/docs/configuration.md new file mode 100644 index 000000000..b7594884d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/configuration.md @@ -0,0 +1,636 @@ +# Configuration and Settings + +dill provides global configuration options and settings that control serialization behavior, protocol selection, and various operational modes for optimal performance and compatibility. + +## Global Settings + +### Settings Dictionary + +```python { .api } +# Global settings dictionary +settings = { + 'protocol': DEFAULT_PROTOCOL, # int: Default pickle protocol version + 'byref': False, # bool: Pickle by reference when possible + 'fmode': 0, # int: File mode setting (0=HANDLE_FMODE, 1=CONTENTS_FMODE, 2=FILE_FMODE) + 'recurse': False, # bool: Recursively pickle nested objects + 'ignore': False # bool: Ignore certain errors during serialization +} +``` + +### Protocol Constants + +```python { .api } +DEFAULT_PROTOCOL = 3 # Default pickle protocol version +HIGHEST_PROTOCOL = 5 # Highest supported pickle protocol version + +# File mode constants +HANDLE_FMODE = 0 # Preserve file handles during serialization +CONTENTS_FMODE = 1 # Save file contents instead of handles +FILE_FMODE = 2 # Save file metadata and paths +``` + +### Extension Control + +```python { .api } +def extend(use_dill=True): + """ + Add or remove dill types to/from the pickle registry. + + Controls whether dill's extended types are available in the standard + pickle module's dispatch table for backward compatibility. + + Parameters: + - use_dill: bool, if True extend dispatch table with dill types, + if False revert to standard pickle types only + + Returns: + None + """ +``` + +## Usage Examples + +### Basic Configuration + +```python +import dill + +# Check current settings +print("Current settings:") +for key, value in dill.settings.items(): + print(f" {key}: {value}") + +# Modify settings +original_protocol = dill.settings['protocol'] +dill.settings['protocol'] = dill.HIGHEST_PROTOCOL +dill.settings['byref'] = True + +print(f"\\nChanged protocol from {original_protocol} to {dill.settings['protocol']}") +print(f"Enabled byref: {dill.settings['byref']}") + +# Test with new settings +def test_function(): + return "Hello with new settings!" + +serialized = dill.dumps(test_function) +restored = dill.loads(serialized) +print(f"Function result: {restored()}") + +# Restore original settings +dill.settings['protocol'] = original_protocol +dill.settings['byref'] = False +``` + +### Protocol Selection and Performance + +```python +import dill +import time + +def benchmark_protocols(obj, protocols=None): + """Benchmark different pickle protocols.""" + if protocols is None: + protocols = [0, 1, 2, 3, 4, dill.HIGHEST_PROTOCOL] + + results = {} + + for protocol in protocols: + try: + # Set protocol + original_protocol = dill.settings['protocol'] + dill.settings['protocol'] = protocol + + # Time serialization + start_time = time.time() + serialized = dill.dumps(obj) + serialize_time = time.time() - start_time + + # Time deserialization + start_time = time.time() + dill.loads(serialized) + deserialize_time = time.time() - start_time + + results[protocol] = { + 'serialize_time': serialize_time, + 'deserialize_time': deserialize_time, + 'size': len(serialized), + 'total_time': serialize_time + deserialize_time + } + + # Restore original protocol + dill.settings['protocol'] = original_protocol + + except Exception as e: + results[protocol] = {'error': str(e)} + + return results + +# Test with complex object +complex_obj = { + 'functions': [lambda x: x**i for i in range(5)], + 'data': list(range(1000)), + 'nested': {'level1': {'level2': [i*j for i in range(10) for j in range(10)]}} +} + +protocol_results = benchmark_protocols(complex_obj) + +print("Protocol Performance Comparison:") +print("-" * 50) +for protocol, result in protocol_results.items(): + if 'error' in result: + print(f"Protocol {protocol}: ERROR - {result['error']}") + else: + print(f"Protocol {protocol}:") + print(f" Serialize: {result['serialize_time']:.4f}s") + print(f" Deserialize: {result['deserialize_time']:.4f}s") + print(f" Size: {result['size']:,} bytes") + print(f" Total: {result['total_time']:.4f}s") +``` + +### File Mode Configuration + +```python +import dill +import tempfile +import io + +def demonstrate_file_modes(): + """Demonstrate different file mode behaviors.""" + + # Create object with file handle + temp_file = tempfile.NamedTemporaryFile(mode='w+', delete=False) + temp_file.write("Test data for file mode demonstration") + temp_file.seek(0) + + obj_with_file = { + 'file_handle': temp_file, + 'file_path': temp_file.name, + 'data': [1, 2, 3, 4, 5] + } + + # Test different file modes + modes = { + dill.HANDLE_FMODE: "HANDLE_FMODE (preserve handles)", + dill.CONTENTS_FMODE: "CONTENTS_FMODE (save contents)", + dill.FILE_FMODE: "FILE_FMODE (save metadata)" + } + + for fmode, description in modes.items(): + print(f"\\nTesting {description}:") + print("-" * 40) + + try: + # Set file mode + original_fmode = dill.settings['fmode'] + dill.settings['fmode'] = fmode + + # Serialize + serialized = dill.dumps(obj_with_file) + print(f"✓ Serialization successful ({len(serialized)} bytes)") + + # Deserialize + restored = dill.loads(serialized) + print(f"✓ Deserialization successful") + + # Test restored file behavior + if 'file_handle' in restored: + file_obj = restored['file_handle'] + if hasattr(file_obj, 'read'): + try: + content = file_obj.read() + print(f"✓ File content: {repr(content[:50])}") + except: + print("✗ File handle not functional") + else: + print("○ File handle replaced with alternative representation") + + # Restore original setting + dill.settings['fmode'] = original_fmode + + except Exception as e: + print(f"✗ Failed: {e}") + + # Cleanup + temp_file.close() + import os + os.unlink(temp_file.name) + +demonstrate_file_modes() +``` + +### Reference vs Value Serialization + +```python +import dill + +def demonstrate_byref_setting(): + """Demonstrate byref setting effects.""" + + # Create shared object + shared_list = [1, 2, 3, 4, 5] + + # Create objects that reference the shared list + obj1 = {'data': shared_list, 'name': 'object1'} + obj2 = {'data': shared_list, 'name': 'object2'} + container = {'obj1': obj1, 'obj2': obj2, 'shared': shared_list} + + # Test with byref=False (default) + print("Testing with byref=False (value serialization):") + dill.settings['byref'] = False + + serialized_value = dill.dumps(container) + restored_value = dill.loads(serialized_value) + + # Modify shared list in restored object + restored_value['shared'].append(999) + + print(f" Original obj1 data: {container['obj1']['data']}") + print(f" Restored obj1 data: {restored_value['obj1']['data']}") + print(f" Restored obj2 data: {restored_value['obj2']['data']}") + print(f" Objects share data in restored: {restored_value['obj1']['data'] is restored_value['obj2']['data']}") + + # Test with byref=True + print("\\nTesting with byref=True (reference serialization):") + dill.settings['byref'] = True + + serialized_ref = dill.dumps(container) + restored_ref = dill.loads(serialized_ref) + + # Modify shared list in restored object + restored_ref['shared'].append(888) + + print(f" Restored obj1 data: {restored_ref['obj1']['data']}") + print(f" Restored obj2 data: {restored_ref['obj2']['data']}") + print(f" Objects share data in restored: {restored_ref['obj1']['data'] is restored_ref['obj2']['data']}") + + print(f"\\nSerialized sizes:") + print(f" Value mode: {len(serialized_value)} bytes") + print(f" Reference mode: {len(serialized_ref)} bytes") + + # Restore default + dill.settings['byref'] = False + +demonstrate_byref_setting() +``` + +### Recursive Serialization Control + +```python +import dill + +def demonstrate_recursive_setting(): + """Demonstrate recursive serialization setting.""" + + # Create nested structure with functions + def outer_function(x): + def inner_function(y): + def innermost_function(z): + return x + y + z + return innermost_function + return inner_function + + nested_func = outer_function(10) + deeply_nested = nested_func(20) + + test_obj = { + 'outer': outer_function, + 'nested': nested_func, + 'deep': deeply_nested, + 'data': {'level1': {'level2': {'level3': [1, 2, 3]}}} + } + + # Test with recurse=False (default) + print("Testing with recurse=False:") + dill.settings['recurse'] = False + + try: + serialized_no_recurse = dill.dumps(test_obj) + restored_no_recurse = dill.loads(serialized_no_recurse) + + # Test functionality + result = restored_no_recurse['deep'](30) + print(f" ✓ Serialization successful, result: {result}") + print(f" Size: {len(serialized_no_recurse)} bytes") + except Exception as e: + print(f" ✗ Failed: {e}") + + # Test with recurse=True + print("\\nTesting with recurse=True:") + dill.settings['recurse'] = True + + try: + serialized_recurse = dill.dumps(test_obj) + restored_recurse = dill.loads(serialized_recurse) + + # Test functionality + result = restored_recurse['deep'](30) + print(f" ✓ Serialization successful, result: {result}") + print(f" Size: {len(serialized_recurse)} bytes") + except Exception as e: + print(f" ✗ Failed: {e}") + + # Restore default + dill.settings['recurse'] = False + +demonstrate_recursive_setting() +``` + +## Advanced Configuration Management + +### Configuration Context Manager + +```python +import dill +from contextlib import contextmanager + +@contextmanager +def dill_config(**settings): + """Context manager for temporary dill configuration.""" + # Save original settings + original = dill.settings.copy() + + # Apply new settings + dill.settings.update(settings) + + try: + yield dill.settings + finally: + # Restore original settings + dill.settings.clear() + dill.settings.update(original) + +# Usage examples +def test_function(): + return "Test result" + +# Temporary high-performance configuration +with dill_config(protocol=dill.HIGHEST_PROTOCOL, byref=True): + print(f"Using protocol: {dill.settings['protocol']}") + serialized = dill.dumps(test_function) + print(f"Serialized size: {len(serialized)} bytes") + +# Back to default settings +print(f"Back to protocol: {dill.settings['protocol']}") + +# Temporary memory-efficient configuration +with dill_config(fmode=dill.CONTENTS_FMODE, recurse=True): + complex_obj = {'data': [1, 2, 3], 'func': lambda x: x*2} + serialized = dill.dumps(complex_obj) + restored = dill.loads(serialized) + print(f"Function result: {restored['func'](5)}") +``` + +### Configuration Profiles + +```python +import dill + +class DillProfile: + """Predefined configuration profiles for different use cases.""" + + # Performance-optimized profile + PERFORMANCE = { + 'protocol': dill.HIGHEST_PROTOCOL, + 'byref': False, + 'fmode': dill.HANDLE_FMODE, + 'recurse': False, + 'ignore': False + } + + # Size-optimized profile + COMPACT = { + 'protocol': dill.HIGHEST_PROTOCOL, + 'byref': True, + 'fmode': dill.CONTENTS_FMODE, + 'recurse': True, + 'ignore': False + } + + # Compatibility profile + COMPATIBLE = { + 'protocol': 2, # Widely supported protocol + 'byref': False, + 'fmode': dill.CONTENTS_FMODE, + 'recurse': False, + 'ignore': True + } + + # Debug profile + DEBUG = { + 'protocol': 0, # Human-readable protocol + 'byref': False, + 'fmode': dill.CONTENTS_FMODE, + 'recurse': True, + 'ignore': False + } + +class ConfigManager: + """Configuration management utility.""" + + def __init__(self): + self.saved_configs = {} + self.current_profile = None + + def apply_profile(self, profile_name): + """Apply a predefined profile.""" + profiles = { + 'performance': DillProfile.PERFORMANCE, + 'compact': DillProfile.COMPACT, + 'compatible': DillProfile.COMPATIBLE, + 'debug': DillProfile.DEBUG + } + + if profile_name in profiles: + # Save current config + self.save_config('pre_profile') + + # Apply profile + dill.settings.update(profiles[profile_name]) + self.current_profile = profile_name + + print(f"Applied {profile_name} profile:") + for key, value in profiles[profile_name].items(): + print(f" {key}: {value}") + else: + print(f"Unknown profile: {profile_name}") + + def save_config(self, name): + """Save current configuration.""" + self.saved_configs[name] = dill.settings.copy() + print(f"Saved configuration as '{name}'") + + def restore_config(self, name): + """Restore saved configuration.""" + if name in self.saved_configs: + dill.settings.clear() + dill.settings.update(self.saved_configs[name]) + self.current_profile = None + print(f"Restored configuration '{name}'") + else: + print(f"No saved configuration named '{name}'") + + def list_configs(self): + """List saved configurations.""" + print("Saved configurations:") + for name in self.saved_configs: + print(f" - {name}") + + def show_current_config(self): + """Show current configuration.""" + print("Current configuration:") + for key, value in dill.settings.items(): + print(f" {key}: {value}") + if self.current_profile: + print(f"Profile: {self.current_profile}") + +# Usage example +config_manager = ConfigManager() + +# Test different profiles +test_obj = { + 'function': lambda x: x**2, + 'data': list(range(100)), + 'nested': {'level1': {'level2': 'deep_value'}} +} + +profiles_to_test = ['performance', 'compact', 'compatible', 'debug'] + +for profile in profiles_to_test: + print(f"\\n{'='*50}") + print(f"Testing {profile.upper()} profile") + print('='*50) + + config_manager.apply_profile(profile) + + try: + serialized = dill.dumps(test_obj) + restored = dill.loads(serialized) + + print(f"✓ Serialization successful") + print(f" Size: {len(serialized):,} bytes") + print(f" Function test: {restored['function'](5)}") + + except Exception as e: + print(f"✗ Failed: {e}") + +# Restore original configuration +config_manager.restore_config('pre_profile') +``` + +### Environment-Specific Configuration + +```python +import dill +import os +import platform +import sys + +class EnvironmentConfig: + """Automatically configure dill based on environment.""" + + @staticmethod + def auto_configure(): + """Automatically configure based on environment.""" + config = {} + + # Python version considerations + if sys.version_info >= (3, 8): + config['protocol'] = dill.HIGHEST_PROTOCOL + else: + config['protocol'] = 3 # Safe for older versions + + # Platform considerations + if platform.system() == 'Windows': + config['fmode'] = dill.CONTENTS_FMODE # Better Windows compatibility + else: + config['fmode'] = dill.HANDLE_FMODE + + # Memory considerations + import psutil + available_memory = psutil.virtual_memory().available + if available_memory < 1024**3: # Less than 1GB + config['byref'] = True # Save memory + config['recurse'] = False + else: + config['byref'] = False + config['recurse'] = True + + # Development vs production + if os.environ.get('DILL_DEBUG'): + config['protocol'] = 0 # Human-readable + config['ignore'] = False # Strict error handling + elif os.environ.get('DILL_PRODUCTION'): + config['ignore'] = True # Lenient error handling + + # Apply configuration + dill.settings.update(config) + + print("Auto-configured dill based on environment:") + for key, value in config.items(): + print(f" {key}: {value}") + + return config + + @staticmethod + def validate_config(): + """Validate current configuration.""" + issues = [] + + # Check protocol compatibility + if dill.settings['protocol'] > 4 and sys.version_info < (3, 8): + issues.append("Protocol 5 requires Python 3.8+") + + # Check memory usage with byref setting + if not dill.settings['byref'] and dill.settings['recurse']: + issues.append("High memory usage: recurse=True with byref=False") + + # Check file mode compatibility + if dill.settings['fmode'] == dill.HANDLE_FMODE and platform.system() == 'Windows': + issues.append("HANDLE_FMODE may have issues on Windows") + + if issues: + print("Configuration issues found:") + for issue in issues: + print(f" ⚠ {issue}") + else: + print("✓ Configuration validation passed") + + return len(issues) == 0 + +# Example usage +print("Current system information:") +print(f" Python: {sys.version}") +print(f" Platform: {platform.system()}") +print(f" Memory: {psutil.virtual_memory().total / (1024**3):.1f}GB") + +# Auto-configure +env_config = EnvironmentConfig() +env_config.auto_configure() + +# Validate configuration +env_config.validate_config() + +# Test with auto-configuration +test_function = lambda x: x * 2 + 1 +serialized = dill.dumps(test_function) +restored = dill.loads(serialized) +print(f"\\nTest result: {restored(10)}") +print(f"Serialized size: {len(serialized)} bytes") +``` + +## Best Practices + +### Configuration Management Guidelines + +1. **Profile Usage**: Use predefined profiles for common scenarios +2. **Context Managers**: Use context managers for temporary configuration changes +3. **Environment Awareness**: Configure based on deployment environment +4. **Performance Testing**: Benchmark different configurations for your use case +5. **Documentation**: Document configuration choices and their rationale + +### Performance Considerations + +1. **Protocol Selection**: Higher protocols generally offer better performance +2. **Memory vs Speed**: `byref=True` saves memory but may slow deserialization +3. **File Mode Impact**: Choose file mode based on your file handling needs +4. **Recursive Overhead**: Enable recursion only when necessary for complex objects \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/core-serialization.md b/.tessl/tiles/tessl/pypi-dill/docs/core-serialization.md new file mode 100644 index 000000000..130a9d6b6 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/core-serialization.md @@ -0,0 +1,285 @@ +# Core Serialization + +dill's core serialization functions provide extended pickle functionality that can handle complex Python objects including functions, classes, lambdas, and nested structures that standard pickle cannot serialize. + +## Primary Serialization Functions + +### File-based Operations + +```python { .api } +def dump(obj, file, protocol=None, byref=None, fmode=None, recurse=None, **kwds): + """ + Serialize object to a file. + + Provides enhanced serialization capabilities beyond standard pickle.dump, + supporting complex objects like functions, classes, and nested structures. + + Parameters: + - obj: object to serialize + - file: file-like object to write to (must have write() method) + - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) + - byref: bool, pickle by reference when possible to reduce file size + - fmode: int, file mode for handle management (0=HANDLE_FMODE, 1=CONTENTS_FMODE, 2=FILE_FMODE) + - recurse: bool, recursively pickle nested objects and their dependencies + - **kwds: additional keyword arguments passed to Pickler + + Returns: + None + + Raises: + - PicklingError: when object cannot be serialized + - IOError: when file operations fail + """ + +def load(file, ignore=None, **kwds): + """ + Deserialize object from a file. + + Provides enhanced deserialization capabilities beyond standard pickle.load, + with improved error handling and support for complex object restoration. + + Parameters: + - file: file-like object to read from (must have read() method) + - ignore: bool, ignore certain unpickling errors and continue + - **kwds: additional keyword arguments passed to Unpickler + + Returns: + object: deserialized object with full functionality restored + + Raises: + - UnpicklingError: when deserialization fails + - IOError: when file operations fail + """ +``` + +### String-based Operations + +```python { .api } +def dumps(obj, protocol=None, byref=None, fmode=None, recurse=None, **kwds): + """ + Serialize object to a bytes string. + + Converts any Python object to a bytes representation that can be + stored, transmitted, or restored later with full functionality. + + Parameters: + - obj: object to serialize (any Python object) + - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) + - byref: bool, pickle by reference when possible to reduce size + - fmode: int, file mode for handle management + - recurse: bool, recursively pickle nested objects and dependencies + - **kwds: additional keyword arguments passed to Pickler + + Returns: + bytes: serialized object as bytes string + + Raises: + - PicklingError: when object cannot be serialized + """ + +def loads(str, ignore=None, **kwds): + """ + Deserialize object from a bytes string. + + Restores a Python object from its bytes representation with + full functionality and state preservation. + + Parameters: + - str: bytes, bytes string containing serialized object + - ignore: bool, ignore certain unpickling errors and continue + - **kwds: additional keyword arguments passed to Unpickler + + Returns: + object: deserialized object with original functionality + + Raises: + - UnpicklingError: when deserialization fails + - TypeError: when input is not bytes + """ +``` + +### Deep Copy Operation + +```python { .api } +def copy(obj, *args, **kwds): + """ + Create a deep copy of an object using serialization. + + Uses dill's enhanced serialization to create deep copies of complex + objects that standard copy.deepcopy cannot handle. + + Parameters: + - obj: object to copy (any Python object) + - *args: positional arguments passed to dumps/loads + - **kwds: keyword arguments passed to dumps/loads + + Returns: + object: deep copy of the input object with independent state + + Raises: + - PicklingError: when object cannot be serialized for copying + """ +``` + +## Usage Examples + +### Basic Function Serialization + +```python +import dill + +# Serialize a function with closure +def create_multiplier(factor): + def multiply(x): + return x * factor + return multiply + +# Create function with closure +times_three = create_multiplier(3) + +# Serialize to bytes +serialized = dill.dumps(times_three) + +# Deserialize and use +restored_function = dill.loads(serialized) +result = restored_function(4) # Returns 12 +``` + +### Class and Instance Serialization + +```python +import dill + +# Define a class with methods +class Calculator: + def __init__(self, precision=2): + self.precision = precision + + def add(self, a, b): + return round(a + b, self.precision) + +# Create instance +calc = Calculator(precision=3) + +# Serialize to file +with open('calculator.pkl', 'wb') as f: + dill.dump(calc, f) + +# Load from file +with open('calculator.pkl', 'rb') as f: + restored_calc = dill.load(f) + +result = restored_calc.add(1.2345, 2.6789) # Returns 3.913 +``` + +### Lambda and Nested Function Handling + +```python +import dill + +# Complex nested structure with lambdas +operations = { + 'square': lambda x: x * x, + 'cube': lambda x: x ** 3, + 'factorial': lambda n: 1 if n <= 1 else n * operations['factorial'](n-1) +} + +# Serialize complex structure +data = dill.dumps(operations) + +# Restore and use +restored_ops = dill.loads(data) +print(restored_ops['square'](5)) # 25 +print(restored_ops['factorial'](5)) # 120 +``` + +### Error Handling + +```python +import dill +from dill import PicklingError, UnpicklingError + +# Handle serialization errors +def safe_serialize(obj): + try: + return dill.dumps(obj) + except PicklingError as e: + print(f"Cannot serialize object: {e}") + return None + +# Handle deserialization errors +def safe_deserialize(data): + try: + return dill.loads(data) + except UnpicklingError as e: + print(f"Cannot deserialize data: {e}") + return None +``` + +## Advanced Features + +### Protocol Selection + +```python +import dill + +# Use specific protocol version +data = dill.dumps(my_object, protocol=dill.HIGHEST_PROTOCOL) + +# Check protocol compatibility +print(f"Default protocol: {dill.DEFAULT_PROTOCOL}") +print(f"Highest protocol: {dill.HIGHEST_PROTOCOL}") +``` + +### File Mode Options + +```python +import dill + +# Different file modes for handle management +obj = complex_object_with_files + +# Handle mode - preserve file handles +dill.dump(obj, file, fmode=dill.HANDLE_FMODE) + +# Contents mode - save file contents +dill.dump(obj, file, fmode=dill.CONTENTS_FMODE) + +# File mode - save file metadata +dill.dump(obj, file, fmode=dill.FILE_FMODE) +``` + +### Recursive Serialization + +```python +import dill + +# Enable recursive pickling for nested dependencies +nested_structure = create_complex_nested_object() + +# Recursively serialize all dependencies +data = dill.dumps(nested_structure, recurse=True) +``` + +## Integration with Standard Library + +dill functions as a drop-in replacement for pickle: + +```python +# Replace pickle with dill +import dill as pickle + +# All pickle functionality works +data = pickle.dumps(obj) +restored = pickle.loads(data) + +# Plus extended capabilities +function_data = pickle.dumps(my_function) # Works with dill, fails with pickle +``` + +## Performance Considerations + +- **Protocol Selection**: Higher protocols generally provide better performance and smaller file sizes +- **Recursive Mode**: Use `recurse=True` cautiously as it can significantly increase serialization time for large object graphs +- **File vs String**: File-based operations are more memory-efficient for large objects +- **Reference Mode**: `byref=True` can reduce file size but may affect object independence after deserialization \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md b/.tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md new file mode 100644 index 000000000..5a877ca22 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md @@ -0,0 +1,467 @@ +# Diagnostic Tools + +dill provides comprehensive diagnostic tools for analyzing serialization capabilities, identifying problematic objects, and debugging pickling issues with detailed error reporting and analysis. + +## Core Diagnostic Functions + +### Pickling Capability Testing + +```python { .api } +def pickles(obj, exact=False, safe=False, **kwds): + """ + Check if an object can be pickled. + + Tests whether an object can be successfully serialized and deserialized + using dill, providing a simple boolean result for compatibility checking. + + Parameters: + - obj: object to test for pickling capability + - exact: bool, use exact type matching for compatibility testing + - safe: bool, use safe mode to avoid side effects during testing + - **kwds: additional keyword arguments passed to dumps/loads + + Returns: + bool: True if object can be pickled and unpickled successfully + + Raises: + - Exception: only when safe=False and testing encounters severe errors + """ + +def check(obj, *args, **kwds): + """ + Check for pickling errors and print diagnostic information. + + Performs comprehensive pickling analysis and displays detailed + information about any issues encountered during serialization testing. + + Parameters: + - obj: object to check for pickling errors + - *args: positional arguments passed to pickles() + - **kwds: keyword arguments passed to pickles() + + Returns: + bool: True if no errors found, False if pickling issues detected + """ +``` + +### Problem Object Detection + +```python { .api } +def baditems(obj, exact=False, safe=False): + """ + Find objects that cannot be pickled within a complex structure. + + Recursively analyzes an object to identify specific items that + prevent successful serialization, helping isolate problematic components. + + Parameters: + - obj: object to analyze for unpickleable items + - exact: bool, use exact type matching for analysis + - safe: bool, use safe mode to avoid side effects + + Returns: + list: list of unpickleable objects found in the structure + """ + +def badobjects(obj, depth=0, exact=False, safe=False): + """ + Get objects that fail to pickle. + + Analyzes object structure at specific depth levels to identify + problematic objects that prevent serialization at different nesting levels. + + Parameters: + - obj: object to analyze + - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) + - exact: bool, use exact type matching + - safe: bool, use safe mode to avoid side effects + + Returns: + object or dict: at depth=0 returns the object if it fails to pickle (None if it pickles), + at depth>0 returns dict mapping attribute names to bad objects + """ + +def badtypes(obj, depth=0, exact=False, safe=False): + """ + Get types for objects that fail to pickle. + + Identifies specific types that cause pickling failures, providing + type-level analysis of serialization compatibility issues. + + Parameters: + - obj: object to analyze for problematic types + - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) + - exact: bool, use exact type matching + - safe: bool, use safe mode + + Returns: + type or dict: at depth=0 returns the type if object fails to pickle (None if it pickles), + at depth>0 returns dict mapping attribute names to problematic types + """ + +def errors(obj, depth=0, exact=False, safe=False): + """ + Get detailed pickling error information. + + Provides comprehensive error analysis including specific error messages, + problematic objects, and suggested solutions for pickling failures. + + Parameters: + - obj: object to analyze for errors + - depth: int, maximum analysis depth (0 for unlimited) + - exact: bool, use exact type matching + - safe: bool, use safe mode to avoid side effects + + Returns: + list: list of detailed error descriptions and diagnostic information + """ +``` + +## Usage Examples + +### Basic Compatibility Testing + +```python +import dill + +# Test various object types +def test_function(): + return "Hello from function" + +class TestClass: + def __init__(self, value): + self.value = value + + def method(self): + return self.value * 2 + +# Test individual objects +print(dill.pickles(test_function)) # True +print(dill.pickles(TestClass)) # True +print(dill.pickles(TestClass(42))) # True + +# Test problematic objects +import threading +lock = threading.Lock() +print(dill.pickles(lock)) # False (locks can't be pickled) + +# Use safe mode for testing +print(dill.pickles(dangerous_object, safe=True)) +``` + +### Comprehensive Error Analysis + +```python +import dill + +# Complex object with potential issues +class ComplexObject: + def __init__(self): + self.data = [1, 2, 3] + self.function = lambda x: x + 1 + self.lock = threading.Lock() # Problematic + self.nested = { + 'good': 'string', + 'bad': open('/dev/null', 'r') # File handle - problematic + } + +complex_obj = ComplexObject() + +# Quick check +if not dill.check(complex_obj): + print("Object has pickling issues") + + # Find problematic items + bad_items = dill.baditems(complex_obj) + print(f"Found {len(bad_items)} unpickleable items:") + for item in bad_items: + print(f" {type(item)}: {item}") + + # Get detailed error information + error_details = dill.errors(complex_obj) + print("\nDetailed errors:") + for error in error_details: + print(f" {error}") +``` + +### Automated Testing Suite + +```python +import dill + +def comprehensive_pickle_test(obj, name="object"): + """Comprehensive pickling analysis for an object.""" + print(f"\nTesting {name}...") + print("=" * 50) + + # Basic pickling test + can_pickle = dill.pickles(obj, safe=True) + print(f"Can pickle: {can_pickle}") + + if not can_pickle: + # Detailed analysis + print("\nProblematic items:") + bad_items = dill.baditems(obj, safe=True) + for i, item in enumerate(bad_items): + print(f" {i+1}. {type(item).__name__}: {repr(item)[:50]}...") + + print("\nProblematic types:") + bad_types = dill.badtypes(obj, safe=True) + for i, bad_type in enumerate(bad_types): + print(f" {i+1}. {bad_type}") + + print("\nError details:") + error_list = dill.errors(obj, safe=True) + for i, error in enumerate(error_list): + print(f" {i+1}. {error}") + + return can_pickle + +# Test various objects +test_objects = { + 'simple_function': lambda x: x + 1, + 'complex_class': ComplexObject(), + 'nested_structure': {'functions': [lambda x: x, lambda y: y**2]}, + 'problematic_object': complex_obj +} + +results = {} +for name, obj in test_objects.items(): + results[name] = comprehensive_pickle_test(obj, name) + +print(f"\nSummary: {sum(results.values())}/{len(results)} objects can be pickled") +``` + +### Interactive Debugging Session + +```python +import dill + +def debug_pickling_issues(obj): + """Interactive debugging for pickling issues.""" + print("Starting pickling diagnosis...") + + # Step 1: Basic test + if dill.pickles(obj): + print("✓ Object can be pickled successfully") + return True + + print("✗ Object cannot be pickled") + + # Step 2: Identify bad items + print("\nSearching for problematic items...") + bad_items = dill.baditems(obj) + + if not bad_items: + print("No specific bad items found - issue may be with object structure") + return False + + print(f"Found {len(bad_items)} problematic items:") + + # Step 3: Categorize issues + by_type = {} + for item in bad_items: + item_type = type(item).__name__ + if item_type not in by_type: + by_type[item_type] = [] + by_type[item_type].append(item) + + # Step 4: Provide solutions + for item_type, items in by_type.items(): + print(f"\n{item_type} objects ({len(items)} found):") + for i, item in enumerate(items[:3]): # Show first 3 + print(f" {i+1}. {repr(item)[:60]}...") + + # Suggest solutions + if item_type == 'Lock': + print(" → Solution: Remove locks or replace with serializable alternatives") + elif item_type == 'TextIOWrapper': + print(" → Solution: Close files or use file paths instead of handles") + elif item_type == 'module': + print(" → Solution: Import modules by name rather than storing references") + else: + print(f" → Solution: Remove {item_type} objects or find serializable alternatives") + + return False + +# Usage +debug_pickling_issues(problematic_object) +``` + +## Advanced Diagnostic Techniques + +### Depth-Based Analysis + +```python +import dill + +def analyze_by_depth(obj, max_depth=5): + """Analyze object pickling issues by depth level.""" + print("Depth-based analysis:") + print("-" * 30) + + for depth in range(max_depth + 1): + bad_objects = dill.badobjects(obj, depth=depth, safe=True) + bad_types = dill.badtypes(obj, depth=depth, safe=True) + + print(f"Depth {depth}:") + print(f" Bad objects: {len(bad_objects)}") + print(f" Bad types: {len(set(t.__name__ for t in bad_types))}") + + if bad_objects: + # Show sample bad objects at this depth + sample = bad_objects[:2] + for obj in sample: + print(f" {type(obj).__name__}: {repr(obj)[:40]}...") + +# Usage +analyze_by_depth(complex_nested_object) +``` + +### Custom Diagnostic Rules + +```python +import dill + +class PicklingDiagnostic: + """Custom diagnostic tool for pickling analysis.""" + + def __init__(self): + self.rules = { + 'file_handles': lambda obj: hasattr(obj, 'read') and hasattr(obj, 'write'), + 'locks': lambda obj: hasattr(obj, 'acquire') and hasattr(obj, 'release'), + 'generators': lambda obj: hasattr(obj, '__next__') and hasattr(obj, '__iter__'), + 'lambda_functions': lambda obj: callable(obj) and obj.__name__ == '', + } + + def diagnose(self, obj): + """Run custom diagnostic rules.""" + issues = {} + + # Test overall pickling + can_pickle = dill.pickles(obj, safe=True) + issues['can_pickle'] = can_pickle + + if can_pickle: + return issues + + # Run custom rules + bad_items = dill.baditems(obj, safe=True) + + for rule_name, rule_func in self.rules.items(): + matching_items = [item for item in bad_items if rule_func(item)] + if matching_items: + issues[rule_name] = matching_items + + return issues + + def report(self, obj, name="object"): + """Generate diagnostic report.""" + issues = self.diagnose(obj) + + print(f"Diagnostic Report for {name}") + print("=" * 40) + + if issues.get('can_pickle', False): + print("✓ Object can be pickled successfully") + return + + print("✗ Object cannot be pickled") + print("\nIssues found:") + + for issue_type, items in issues.items(): + if issue_type == 'can_pickle': + continue + + print(f"\n{issue_type.replace('_', ' ').title()}:") + for item in items[:3]: # Show first 3 + print(f" - {type(item).__name__}: {repr(item)[:50]}...") + +# Usage +diagnostic = PicklingDiagnostic() +diagnostic.report(complex_object, "MyComplexObject") +``` + +### Performance Impact Analysis + +```python +import dill +import time + +def performance_diagnostic(obj, name="object"): + """Analyze performance impact of pickling.""" + print(f"Performance analysis for {name}") + print("-" * 40) + + # Test if object can be pickled + if not dill.pickles(obj, safe=True): + print("Object cannot be pickled - skipping performance test") + return + + # Measure serialization time + start_time = time.time() + serialized = dill.dumps(obj) + serialize_time = time.time() - start_time + + # Measure deserialization time + start_time = time.time() + dill.loads(serialized) + deserialize_time = time.time() - start_time + + # Report results + print(f"Serialization time: {serialize_time:.4f} seconds") + print(f"Deserialization time: {deserialize_time:.4f} seconds") + print(f"Serialized size: {len(serialized):,} bytes") + print(f"Compression ratio: {len(str(obj).encode()) / len(serialized):.2f}x") + +# Usage with different objects +performance_diagnostic(large_data_structure, "Large Data Structure") +performance_diagnostic(complex_function, "Complex Function") +performance_diagnostic(class_instance, "Class Instance") +``` + +## Integration with Development Workflow + +### Pre-commit Pickling Validation + +```python +import dill +import sys + +def validate_pickling(modules_to_test): + """Validate that key objects can be pickled before commit.""" + print("Running pickling validation...") + + failed_objects = [] + + for module_name in modules_to_test: + try: + module = __import__(module_name) + + # Test key objects in module + for attr_name in dir(module): + if not attr_name.startswith('_'): + obj = getattr(module, attr_name) + + if callable(obj) or hasattr(obj, '__dict__'): + if not dill.pickles(obj, safe=True): + failed_objects.append(f"{module_name}.{attr_name}") + + except ImportError: + print(f"Could not import {module_name}") + + if failed_objects: + print("❌ Pickling validation failed for:") + for obj in failed_objects: + print(f" - {obj}") + return False + else: + print("✓ All objects pass pickling validation") + return True + +# Usage in CI/CD pipeline +if __name__ == "__main__": + modules = ["mypackage.core", "mypackage.utils", "mypackage.models"] + success = validate_pickling(modules) + sys.exit(0 if success else 1) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/index.md b/.tessl/tiles/tessl/pypi-dill/docs/index.md new file mode 100644 index 000000000..7e9342e50 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/index.md @@ -0,0 +1,453 @@ +# dill + +dill is an extended Python serialization library that extends Python's pickle module to handle a broader range of Python objects including functions with yields, nested functions, lambdas, classes, and more exotic Python types. It provides a drop-in replacement for pickle with enhanced capabilities for serializing complex Python objects that standard pickle cannot handle, making it particularly useful for distributed computing, parallel processing, and saving interpreter sessions. + +The library offers additional features like source code inspection, interactive pickling diagnostics, and the ability to save and restore complete interpreter sessions, with applications in scientific computing, debugging, and development tools that require comprehensive Python object serialization. + +## Package Information + +- **Package Name**: dill +- **Language**: Python +- **Installation**: `pip install dill` +- **Optional Features**: `pip install dill[graph]` for object graph diagnostics, `pip install dill[profile]` for profiling tools + +## Core Imports + +```python +import dill +``` + +Common usage patterns: + +```python +import dill as pickle # Drop-in replacement for pickle +from dill import dump, dumps, load, loads # Direct function imports +``` + +## Basic Usage + +```python +import dill + +# Basic serialization - works like pickle but handles more types +def example_function(): + return "Hello from dill!" + +# Serialize function to bytes +serialized = dill.dumps(example_function) + +# Deserialize function +restored_function = dill.loads(serialized) +result = restored_function() # "Hello from dill!" + +# Serialize to file +with open('function.pkl', 'wb') as f: + dill.dump(example_function, f) + +# Load from file +with open('function.pkl', 'rb') as f: + loaded_function = dill.load(f) +``` + +## Architecture + +dill is designed as an extended version of Python's pickle module, maintaining full compatibility while adding support for previously unpickleable objects. The architecture consists of: + +- **Core Serialization Engine**: Extended pickler/unpickler classes that handle complex Python objects +- **Type Registry System**: Automatic detection and registration of new types for serialization +- **Session Management**: Capability to save and restore entire interpreter sessions including modules +- **Source Code Analysis**: Tools for inspecting and extracting source code from objects +- **Diagnostic Framework**: Utilities for identifying and debugging serialization issues + +dill integrates seamlessly with the broader Python ecosystem, supporting distributed computing frameworks like multiprocessing, concurrent.futures, and third-party libraries like Celery and Dask. + +## Capabilities + +### Core Serialization + +Primary serialization and deserialization functions that extend pickle's capabilities to handle complex Python objects including functions, classes, and nested structures. + +```python { .api } +def dump(obj, file, protocol=None, byref=None, fmode=None, recurse=None, **kwds): + """ + Serialize object to a file. + + Parameters: + - obj: object to serialize + - file: file-like object to write to + - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) + - byref: bool, pickle by reference when possible + - fmode: int, file mode for handle management + - recurse: bool, recursively pickle nested objects + - **kwds: additional keyword arguments + + Returns: + None + """ + +def dumps(obj, protocol=None, byref=None, fmode=None, recurse=None, **kwds): + """ + Serialize object to a bytes string. + + Parameters: + - obj: object to serialize + - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) + - byref: bool, pickle by reference when possible + - fmode: int, file mode for handle management + - recurse: bool, recursively pickle nested objects + - **kwds: additional keyword arguments + + Returns: + bytes: serialized object as bytes string + """ + +def load(file, ignore=None, **kwds): + """ + Deserialize object from a file. + + Parameters: + - file: file-like object to read from + - ignore: bool, ignore certain unpickling errors + - **kwds: additional keyword arguments + + Returns: + object: deserialized object + """ + +def loads(str, ignore=None, **kwds): + """ + Deserialize object from a bytes string. + + Parameters: + - str: bytes string containing serialized object + - ignore: bool, ignore certain unpickling errors + - **kwds: additional keyword arguments + + Returns: + object: deserialized object + """ + +def copy(obj, *args, **kwds): + """ + Create a deep copy of an object using serialization. + + Parameters: + - obj: object to copy + - *args: positional arguments passed to dumps/loads + - **kwds: keyword arguments passed to dumps/loads + + Returns: + object: deep copy of the input object + """ +``` + +[Core Serialization](./core-serialization.md) + +### Extended Pickler Classes + +Enhanced Pickler and Unpickler classes that provide fine-grained control over the serialization process and support for complex Python objects. + +```python { .api } +class Pickler: + """ + Extended pickler with additional capabilities for complex objects. + + Provides enhanced serialization support beyond standard pickle.Pickler, + including functions, classes, and other previously unpickleable types. + """ + +class Unpickler: + """ + Extended unpickler with additional capabilities for complex objects. + + Provides enhanced deserialization support beyond standard pickle.Unpickler, + with improved error handling and type restoration. + """ +``` + +[Extended Pickler Classes](./pickler-classes.md) + +### Session Management + +Functions for saving and restoring complete interpreter sessions and individual modules, enabling persistence of development environments and interactive workflows. + +```python { .api } +def dump_session(filename=None, main=None, byref=False, **kwds): + """ + Save interpreter session to a file. + + Parameters: + - filename: str, output filename (default: temporary file) + - main: module, main module to save (default: __main__) + - byref: bool, pickle by reference when possible + - **kwds: additional keyword arguments + + Returns: + None + """ + +def load_session(filename=None, main=None, **kwds): + """ + Load interpreter session from a file. + + Parameters: + - filename: str, input filename + - main: module, target module (default: __main__) + - **kwds: additional keyword arguments + + Returns: + None + """ + +def dump_module(name=None, module=None, main=None, **kwds): + """ + Save a module to a file. + + Parameters: + - name: str, output filename + - module: module, module object to save + - main: module, main module context + - **kwds: additional keyword arguments + + Returns: + None + """ + +def load_module(name=None, module=None, main=None, **kwds): + """ + Load a module from a file. + + Parameters: + - name: str, input filename + - module: str, module name to load into + - main: module, main module context + - **kwds: additional keyword arguments + + Returns: + module: loaded module object + """ +``` + +[Session Management](./session-management.md) + +### Source Code Analysis + +Tools for extracting, analyzing, and manipulating source code from Python objects, enabling introspection and code generation capabilities. + +```python { .api } +def getsource(object, alias='', lstrip=False, enclosing=False, force=False, builtin=False): + """ + Get source code for an object. + + Parameters: + - object: object to get source for (module, class, method, function, traceback, frame, or code object) + - alias: str, alias name for the object (adds line of code that renames the object) + - lstrip: bool, ensure there is no indentation in the first line of code + - enclosing: bool, include enclosing code and dependencies + - force: bool, catch (TypeError,IOError) and try to use import hooks + - builtin: bool, force an import for any builtins + + Returns: + str: source code as single string + + Raises: + - IOError: when source code cannot be retrieved + - TypeError: for objects where source code is unavailable (e.g. builtins) + """ + +def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): + """ + Get the likely import string for the given object. + + Parameters: + - obj: object to inspect and generate import for + - alias: str, alias name to use (renames the object on import) + - verify: bool, test the import string before returning it + - builtin: bool, force an import for builtins where possible + - enclosing: bool, get the import for the outermost enclosing callable + + Returns: + str: import statement as string + """ +``` + +[Source Code Analysis](./source-analysis.md) + +### Diagnostic Tools + +Utilities for analyzing serialization capabilities, identifying problems, and debugging pickling issues with detailed error reporting. + +```python { .api } +def pickles(obj, exact=False, safe=False, **kwds): + """ + Check if an object can be pickled. + + Parameters: + - obj: object to test for pickling capability + - exact: bool, use exact type matching for compatibility testing + - safe: bool, use safe mode to avoid side effects during testing + - **kwds: additional keyword arguments passed to dumps/loads + + Returns: + bool: True if object can be pickled and unpickled successfully + """ + +def check(obj, *args, **kwds): + """ + Check for pickling errors and print diagnostic information. + + Parameters: + - obj: object to check for pickling errors + - *args: positional arguments passed to pickles() + - **kwds: keyword arguments passed to pickles() + + Returns: + bool: True if no errors found, False if pickling issues detected + """ + +def baditems(obj, exact=False, safe=False): + """ + Find objects that cannot be pickled within a complex structure. + + Parameters: + - obj: object to analyze for unpickleable items + - exact: bool, use exact type matching for analysis + - safe: bool, use safe mode to avoid side effects + + Returns: + list: list of unpickleable objects found in the structure + """ + +def badobjects(obj, depth=0, exact=False, safe=False): + """ + Get objects that fail to pickle. + + Parameters: + - obj: object to analyze + - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) + - exact: bool, use exact type matching + - safe: bool, use safe mode to avoid side effects + + Returns: + object or dict: at depth=0 returns the object if it fails to pickle (None if it pickles), + at depth>0 returns dict mapping attribute names to bad objects + """ + +def errors(obj, depth=0, exact=False, safe=False): + """ + Get detailed pickling error information. + + Parameters: + - obj: object to analyze for errors + - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) + - exact: bool, use exact type matching + - safe: bool, use safe mode to avoid side effects + + Returns: + Exception or dict: detailed error information for pickling failures + """ +``` + +[Diagnostic Tools](./diagnostic-tools.md) + +### Type Registry System + +Functions for registering custom types and extending dill's serialization capabilities to handle new object types. + +```python { .api } +def register(t): + """ + Register a type with the pickler. + + Parameters: + - t: type, type to register + + Returns: + function: decorator function + """ + +def pickle(t, func): + """ + Add a type to the pickle dispatch table. + + Parameters: + - t: type, type to add + - func: function, pickling function for the type + + Returns: + None + """ + +def extend(use_dill=True): + """ + Add or remove dill types to/from the pickle registry. + + Parameters: + - use_dill: bool, if True extend dispatch table, if False revert + + Returns: + None + """ +``` + +[Type Registry System](./type-registry.md) + +### Temporary Operations + +Utilities for temporary file operations with serialization, stream capture, and IO buffer management for testing and development workflows. + +```python { .api } +def dump(object, **kwds): + """ + Dump object to a NamedTemporaryFile using dill.dump. + + Parameters: + - object: object to serialize to temporary file + - **kwds: optional keyword arguments including suffix, prefix, and NamedTemporaryFile options + + Returns: + file handle: NamedTemporaryFile handle containing serialized object + """ + +def load(file, **kwds): + """ + Load an object that was stored with dill.temp.dump. + + Parameters: + - file: file handle or str, file handle or path to file containing serialized object + - **kwds: optional keyword arguments including mode ('r' or 'rb', default: 'rb') + + Returns: + object: deserialized object + """ + +def capture(stream='stdout'): + """ + Capture stdout or stderr stream. + + Parameters: + - stream: str, stream name ('stdout' or 'stderr') + + Returns: + context manager for stream capture + """ +``` + +[Temporary Operations](./temp-operations.md) + +### Configuration and Settings + +Global configuration options and settings that control dill's behavior, protocol selection, and serialization modes. + +```python { .api } +# Global settings dictionary +settings = { + 'protocol': DEFAULT_PROTOCOL, # Default pickle protocol + 'byref': False, # Pickle by reference + 'fmode': 0, # File mode setting + 'recurse': False, # Recursive pickling + 'ignore': False # Ignore errors +} +``` + +[Configuration and Settings](./configuration.md) \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md b/.tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md new file mode 100644 index 000000000..c5c937524 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md @@ -0,0 +1,344 @@ +# Extended Pickler Classes + +dill provides enhanced Pickler and Unpickler classes that extend Python's standard pickle classes with support for complex objects including functions, classes, lambdas, and other previously unpickleable types. + +## Enhanced Pickler Class + +```python { .api } +class Pickler: + """ + Extended pickler with additional capabilities for complex objects. + + Provides enhanced serialization support beyond standard pickle.Pickler, + including functions, classes, nested structures, and other complex types + that standard pickle cannot handle. + + Attributes: + - memo: dict, memoization cache for object references + - bin: bool, binary mode flag + - fast: bool, fast mode for performance optimization + - dispatch_table: dict, custom type dispatch table + + Methods: + - dump(obj): serialize object to file + - save(obj): internal save method + - persistent_id(obj): handle persistent object references + """ +``` + +## Enhanced Unpickler Class + +```python { .api } +class Unpickler: + """ + Extended unpickler with additional capabilities for complex objects. + + Provides enhanced deserialization support beyond standard pickle.Unpickler, + with improved error handling, type restoration, and support for complex + object reconstruction. + + Attributes: + - memo: dict, memoization cache for object reconstruction + - encoding: str, text encoding for string objects + - errors: str, error handling mode + + Methods: + - load(): deserialize object from file + - persistent_load(pid): handle persistent object loading + """ +``` + +## Usage Examples + +### Basic Pickler Usage + +```python +import dill +import io + +# Create a function with closure +def create_counter(start=0): + count = start + def increment(): + nonlocal count + count += 1 + return count + return increment + +counter = create_counter(10) + +# Use Pickler class directly +buffer = io.BytesIO() +pickler = dill.Pickler(buffer) +pickler.dump(counter) + +# Use Unpickler class directly +buffer.seek(0) +unpickler = dill.Unpickler(buffer) +restored_counter = unpickler.load() + +print(restored_counter()) # 11 +print(restored_counter()) # 12 +``` + +### Custom Protocol and Options + +```python +import dill +import io + +# Advanced pickler configuration +buffer = io.BytesIO() +pickler = dill.Pickler(buffer, protocol=dill.HIGHEST_PROTOCOL) + +# Configure pickler options through settings +original_settings = dill.settings.copy() +dill.settings['byref'] = True +dill.settings['recurse'] = True + +complex_object = create_complex_nested_structure() +pickler.dump(complex_object) + +# Restore original settings +dill.settings.update(original_settings) + +# Unpickle with custom error handling +buffer.seek(0) +unpickler = dill.Unpickler(buffer) +try: + restored_object = unpickler.load() +except dill.UnpicklingError as e: + print(f"Unpickling failed: {e}") +``` + +### Integration with File Objects + +```python +import dill + +# Direct file usage +with open('complex_data.pkl', 'wb') as f: + pickler = dill.Pickler(f) + pickler.dump(my_function) + pickler.dump(my_class) + pickler.dump(my_instance) + +# Load multiple objects +with open('complex_data.pkl', 'rb') as f: + unpickler = dill.Unpickler(f) + restored_function = unpickler.load() + restored_class = unpickler.load() + restored_instance = unpickler.load() +``` + +## Advanced Features + +### Custom Dispatch Tables + +```python +import dill +import io + +# Custom type handling +def save_custom_type(pickler, obj): + # Custom serialization logic + pickler.write(b'custom_marker') + pickler.save(obj.__dict__) + +# Create pickler with custom dispatch +buffer = io.BytesIO() +pickler = dill.Pickler(buffer) + +# Add custom type handler +if not hasattr(pickler, 'dispatch_table'): + pickler.dispatch_table = {} +pickler.dispatch_table[MyCustomType] = save_custom_type + +# Use with custom type +custom_obj = MyCustomType() +pickler.dump(custom_obj) +``` + +### Memory-Efficient Streaming + +```python +import dill + +def serialize_large_dataset(data_iterator, filename): + """Serialize large dataset in streaming fashion.""" + with open(filename, 'wb') as f: + pickler = dill.Pickler(f) + + # Stream objects one by one + for item in data_iterator: + pickler.dump(item) + +def deserialize_large_dataset(filename): + """Deserialize large dataset in streaming fashion.""" + with open(filename, 'rb') as f: + unpickler = dill.Unpickler(f) + + while True: + try: + item = unpickler.load() + yield item + except EOFError: + break + +# Usage +large_data = [complex_object(i) for i in range(10000)] +serialize_large_dataset(large_data, 'large_dataset.pkl') + +# Process items one by one without loading all into memory +for item in deserialize_large_dataset('large_dataset.pkl'): + process_item(item) +``` + +## Error Handling and Debugging + +```python +import dill +import io +from dill import PicklingError, UnpicklingError + +def safe_pickle_with_diagnostics(obj, buffer): + """Pickle with comprehensive error handling.""" + try: + pickler = dill.Pickler(buffer) + pickler.dump(obj) + return True + except PicklingError as e: + print(f"Pickling failed: {e}") + + # Use diagnostic tools + bad_items = dill.detect.baditems(obj) + if bad_items: + print("Unpickleable items found:") + for item in bad_items: + print(f" {item}") + + return False + except Exception as e: + print(f"Unexpected error: {e}") + return False + +def safe_unpickle_with_recovery(buffer): + """Unpickle with error recovery.""" + try: + buffer.seek(0) + unpickler = dill.Unpickler(buffer) + return unpickler.load() + except UnpicklingError as e: + print(f"Unpickling failed: {e}") + return None + except Exception as e: + print(f"Unexpected error during unpickling: {e}") + return None +``` + +## Performance Optimization + +```python +import dill +import io + +# Optimize for speed +def fast_pickle_config(): + """Configure dill for maximum speed.""" + # Use highest protocol + protocol = dill.HIGHEST_PROTOCOL + + # Configure for speed over size + settings = { + 'protocol': protocol, + 'byref': False, # Avoid reference resolution overhead + 'recurse': False # Avoid deep recursion overhead + } + + return settings + +# Optimize for size +def compact_pickle_config(): + """Configure dill for minimum size.""" + settings = { + 'protocol': dill.HIGHEST_PROTOCOL, + 'byref': True, # Use references to reduce duplication + 'recurse': True # Ensure complete object graphs + } + + return settings + +# Apply configuration +fast_settings = fast_pickle_config() +buffer = io.BytesIO() + +# Create optimized pickler +pickler = dill.Pickler(buffer, protocol=fast_settings['protocol']) + +# Apply settings through global configuration +original_settings = dill.settings.copy() +dill.settings.update(fast_settings) + +try: + pickler.dump(large_complex_object) +finally: + # Restore original settings + dill.settings.update(original_settings) +``` + +## Thread Safety Considerations + +```python +import dill +import threading +import io + +class ThreadSafePickler: + """Thread-safe wrapper for dill pickler operations.""" + + def __init__(self): + self._lock = threading.Lock() + + def dump_to_bytes(self, obj, **kwargs): + """Thread-safe serialization to bytes.""" + with self._lock: + return dill.dumps(obj, **kwargs) + + def load_from_bytes(self, data, **kwargs): + """Thread-safe deserialization from bytes.""" + with self._lock: + return dill.loads(data, **kwargs) + + def dump_to_file(self, obj, filename, **kwargs): + """Thread-safe serialization to file.""" + with self._lock: + with open(filename, 'wb') as f: + pickler = dill.Pickler(f) + pickler.dump(obj) + + def load_from_file(self, filename, **kwargs): + """Thread-safe deserialization from file.""" + with self._lock: + with open(filename, 'rb') as f: + unpickler = dill.Unpickler(f) + return unpickler.load() + +# Usage in multithreaded environment +safe_pickler = ThreadSafePickler() + +def worker_thread(obj, thread_id): + data = safe_pickler.dump_to_bytes(obj) + restored = safe_pickler.load_from_bytes(data) + print(f"Thread {thread_id}: Object successfully serialized and restored") + +# Start multiple threads +threads = [] +for i in range(5): + t = threading.Thread(target=worker_thread, args=(complex_object, i)) + threads.append(t) + t.start() + +# Wait for completion +for t in threads: + t.join() +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/session-management.md b/.tessl/tiles/tessl/pypi-dill/docs/session-management.md new file mode 100644 index 000000000..e371a19db --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/session-management.md @@ -0,0 +1,361 @@ +# Session Management + +dill provides powerful capabilities for saving and restoring complete interpreter sessions and individual modules, enabling persistence of development environments, interactive workflows, and computational state. + +## Session Operations + +### Complete Session Management + +```python { .api } +def dump_session(filename=None, main=None, byref=False, **kwds): + """ + Save complete interpreter session to a file (deprecated). + + DEPRECATED: This function has been renamed to dump_module(). + Captures the entire state of the Python interpreter including all + variables, functions, classes, and imported modules from the main namespace. + + Parameters: + - filename: str, output filename (default: auto-generated in temp directory) + - main: module, main module to save (default: __main__ module) + - byref: bool, pickle objects by reference when possible + - **kwds: additional keyword arguments passed to dump_module + + Returns: + None + + Raises: + - PicklingError: when session cannot be serialized + - IOError: when file operations fail + """ + +def load_session(filename=None, main=None, **kwds): + """ + Load complete interpreter session from a file (deprecated). + + DEPRECATED: This function has been renamed to load_module(). + Restores the entire state of a Python interpreter session including + all variables, functions, classes, and module dependencies. + + Parameters: + - filename: str, input filename to load from + - main: module, target module to restore into (default: __main__) + - **kwds: additional keyword arguments passed to load_module + + Returns: + None (modifies current interpreter state) + + Raises: + - UnpicklingError: when session cannot be deserialized + - IOError: when file operations fail + """ +``` + +### Individual Module Management + +```python { .api } +def dump_module(filename=None, module=None, refimported=False, **kwds): + """ + Save a specific module to a file. + + Serializes a module's complete state including all attributes, + functions, classes, and internal variables while preserving + cross-module references. + + Parameters: + - filename: str or PathLike, output filename for module dump + - module: module or str, specific module object to save (default: __main__) + - refimported: bool, save imported modules by reference when possible + - **kwds: additional keyword arguments passed to Pickler + + Returns: + None + + Raises: + - PicklingError: when module cannot be serialized + - IOError: when file operations fail + """ + +def load_module(name=None, module=None, main=None, **kwds): + """ + Load a module from a file. + + Restores a module's complete state and integrates it into the + current interpreter environment with proper reference resolution. + + Parameters: + - name: str, input filename containing module dump + - module: str, name for the restored module in sys.modules + - main: module, main module context for integration + - **kwds: additional keyword arguments passed to Unpickler + + Returns: + module: loaded module object + + Raises: + - UnpicklingError: when module cannot be deserialized + - IOError: when file operations fail + """ + +def load_module_asdict(name=None, main=None, **kwds): + """ + Load a module from a file as a dictionary. + + Restores module contents as a dictionary rather than a module object, + useful for inspecting module contents without side effects. + + Parameters: + - name: str, input filename containing module dump + - main: module, main module context for reference resolution + - **kwds: additional keyword arguments passed to load_module + + Returns: + dict: module contents as dictionary + + Raises: + - UnpicklingError: when module cannot be deserialized + - IOError: when file operations fail + """ +``` + +## Usage Examples + +### Interactive Session Persistence + +```python +import dill + +# During interactive session, define some variables and functions +x = 42 +data = [1, 2, 3, 4, 5] + +def process_data(items): + return [item * 2 for item in items] + +class DataProcessor: + def __init__(self, multiplier=1): + self.multiplier = multiplier + + def process(self, data): + return [x * self.multiplier for x in data] + +processor = DataProcessor(3) + +# Save entire session +dill.dump_session('my_analysis_session.pkl') + +# Later, in a new Python session: +import dill +dill.load_session('my_analysis_session.pkl') + +# All variables and functions are now available +result = process_data(data) # [2, 4, 6, 8, 10] +processed = processor.process(data) # [3, 6, 9, 12, 15] +``` + +### Module-specific Operations + +```python +import dill +import mymodule + +# Save specific module +dill.dump_module('mymodule_backup.pkl', module=mymodule) + +# Modify module during development +mymodule.some_function = lambda x: x * 10 + +# Restore original module state +original_module = dill.load_module('mymodule_backup.pkl') +``` + +### Development Workflow Integration + +```python +import dill +import sys + +# Save current development state +def save_checkpoint(name): + """Save current session as a development checkpoint.""" + filename = f'checkpoint_{name}.pkl' + dill.dump_session(filename) + print(f"Session saved to {filename}") + +def load_checkpoint(name): + """Load a development checkpoint.""" + filename = f'checkpoint_{name}.pkl' + dill.load_session(filename) + print(f"Session loaded from {filename}") + +# Usage in development +save_checkpoint('before_experiment') + +# Run experiments, make changes... + +# Restore if needed +load_checkpoint('before_experiment') +``` + +### Module Inspection + +```python +import dill + +# Load module as dictionary for inspection +module_dict = dill.load_module_asdict('saved_module.pkl') + +# Inspect contents without side effects +print("Module contents:") +for name, obj in module_dict.items(): + if not name.startswith('_'): + print(f" {name}: {type(obj)}") + +# Selectively restore specific items +my_function = module_dict.get('my_function') +if my_function: + # Use the function without restoring entire module + result = my_function(input_data) +``` + +## Advanced Features + +### Session Filtering + +```python +import dill + +# Custom session dumping with filtering +def dump_filtered_session(filename, exclude_patterns=None): + """Dump session excluding certain patterns.""" + import __main__ + + # Get current main module + main_dict = __main__.__dict__.copy() + + # Filter out unwanted items + if exclude_patterns: + filtered_dict = {} + for name, obj in main_dict.items(): + if not any(pattern in name for pattern in exclude_patterns): + filtered_dict[name] = obj + + # Temporarily replace main dict + original_dict = __main__.__dict__ + __main__.__dict__ = filtered_dict + + try: + dill.dump_session(filename) + finally: + __main__.__dict__ = original_dict + else: + dill.dump_session(filename) + +# Usage +dump_filtered_session('clean_session.pkl', exclude_patterns=['_temp', 'debug_']) +``` + +### Cross-Environment Session Transfer + +```python +import dill +import sys +import os + +# Save session with environment info +def save_portable_session(filename): + """Save session with environment metadata.""" + # Save session + dill.dump_session(filename) + + # Save environment info + env_info = { + 'python_version': sys.version, + 'platform': sys.platform, + 'path': sys.path.copy(), + 'modules': list(sys.modules.keys()), + 'cwd': os.getcwd() + } + + env_filename = filename.replace('.pkl', '_env.pkl') + with open(env_filename, 'wb') as f: + dill.dump(env_info, f) + +def load_portable_session(filename): + """Load session with environment validation.""" + # Load environment info + env_filename = filename.replace('.pkl', '_env.pkl') + + try: + with open(env_filename, 'rb') as f: + env_info = dill.load(f) + + # Validate compatibility + if env_info['python_version'] != sys.version: + print(f"Warning: Python version mismatch") + print(f" Saved: {env_info['python_version']}") + print(f" Current: {sys.version}") + + # Load session + dill.load_session(filename) + + except FileNotFoundError: + print("Environment info not found, loading session anyway...") + dill.load_session(filename) +``` + +## Integration with Development Tools + +### Jupyter Notebook Integration + +```python +# In Jupyter notebook cells +import dill + +# Save notebook state +def save_notebook_session(): + """Save current notebook session.""" + dill.dump_session('notebook_session.pkl') + print("Notebook session saved") + +def load_notebook_session(): + """Load notebook session.""" + dill.load_session('notebook_session.pkl') + print("Notebook session loaded") + +# Magic command integration (requires IPython) +from IPython.core.magic import register_line_magic + +@register_line_magic +def save_dill(line): + """Magic command to save session.""" + filename = line.strip() or 'session.pkl' + dill.dump_session(filename) + print(f"Session saved to {filename}") + +@register_line_magic +def load_dill(line): + """Magic command to load session.""" + filename = line.strip() or 'session.pkl' + dill.load_session(filename) + print(f"Session loaded from {filename}") + +# Usage: %save_dill my_session.pkl +``` + +## Best Practices + +### Session Management Guidelines + +- **Regular Checkpoints**: Save session state before major experiments or changes +- **Descriptive Naming**: Use meaningful filenames that describe the session state +- **Environment Documentation**: Include metadata about the environment when sharing sessions +- **Selective Restoration**: Use `load_module_asdict` to inspect before full restoration +- **Clean-up**: Remove temporary variables before saving to reduce file size + +### Performance Considerations + +- **Large Sessions**: Break down large sessions into smaller, focused modules +- **Memory Usage**: Be aware that session files can be large for complex environments +- **Loading Time**: Session restoration can take time for environments with many dependencies +- **Cross-platform**: Test session files across different platforms if portability is needed \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/source-analysis.md b/.tessl/tiles/tessl/pypi-dill/docs/source-analysis.md new file mode 100644 index 000000000..1c9f3757c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/source-analysis.md @@ -0,0 +1,576 @@ +# Source Code Analysis + +dill provides powerful tools for extracting, analyzing, and manipulating source code from Python objects, enabling introspection, code generation, and dynamic analysis capabilities. + +## Source Code Extraction + +### Primary Source Functions + +```python { .api } +def getsource(object, alias='', lstrip=False, enclosing=False, force=False, builtin=False): + """ + Get source code for an object. + + Extracts the source code of functions, classes, methods, and other + code objects, providing string representation of the original code. + The source code for interactively-defined objects are extracted from + the interpreter's history. + + Parameters: + - object: object to get source for (module, class, method, function, traceback, frame, or code object) + - alias: str, alias name for the object (adds line of code that renames the object) + - lstrip: bool, ensure there is no indentation in the first line of code + - enclosing: bool, include enclosing code and dependencies + - force: bool, catch (TypeError,IOError) and try to use import hooks + - builtin: bool, force an import for any builtins + + Returns: + str: source code as single string + + Raises: + - IOError: when source code cannot be retrieved + - TypeError: for objects where source code is unavailable (e.g. builtins) + """ + +def getsourcelines(object, lstrip=False, enclosing=False): + """ + Get source lines for an object. + + Returns the source code as a list of lines along with the starting + line number in the original file. + + Parameters: + - object: object to get source lines for + - lstrip: bool, left-strip whitespace from source lines + - enclosing: bool, include enclosing scope + + Returns: + tuple: (list of source lines, starting line number) + + Raises: + - OSError: when source cannot be found + """ + +def findsource(object): + """ + Find source code for an object. + + Locate the source code for an object by examining the file system, + module imports, and other available sources. + + Parameters: + - object: object to find source for + + Returns: + tuple: (list of all source lines, starting line number) + + Raises: + - OSError: when source cannot be located + """ +``` + +### Source Code Utilities + +```python { .api } +def getblocks(object, lstrip=False, enclosing=False, locate=False): + """ + Get code blocks for an object. + + Extracts logical code blocks (functions, classes, methods) associated + with an object, providing structured access to code components. + + Parameters: + - object: object to get code blocks for + - lstrip: bool, left-strip whitespace from blocks + - enclosing: bool, include enclosing blocks + - locate: bool, include location information + + Returns: + list: list of code blocks with metadata + """ + +def dumpsource(object, alias='', new=False, enclose=True): + """ + Dump source code with additional metadata. + + Creates a comprehensive source dump including dependencies, + imports, and context information needed for code reconstruction. + + Parameters: + - object: object to dump source for + - alias: str, alias name for the object + - new: bool, create new-style source dump format + - enclose: bool, enclose source with additional context + + Returns: + str: comprehensive source dump + """ +``` + +## Import Analysis + +### Import Statement Generation + +```python { .api } +def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): + """ + Get the likely import string for the given object. + + Generates the appropriate import statement needed to access an object, + including module path resolution and alias handling. + + Parameters: + - obj: object to inspect and generate import for + - alias: str, alias name to use (renames the object on import) + - verify: bool, test the import string before returning it + - builtin: bool, force an import for builtins where possible + - enclosing: bool, get the import for the outermost enclosing callable + + Returns: + str: import statement as string + """ + +def getimportable(obj, alias='', byname=True, explicit=False): + """ + Get importable representation of an object. + + Creates a representation that can be used to recreate the object + through import statements and attribute access. + + Parameters: + - obj: object to make importable + - alias: str, alias name to use + - byname: bool, prefer name-based imports over direct references + - explicit: bool, use explicit import paths + + Returns: + str: importable representation + """ + +def likely_import(obj, passive=False, explicit=False): + """ + Get likely import statement for an object. + + Attempts to determine the most likely import statement that would + provide access to the given object. + + Parameters: + - obj: object to analyze + - passive: bool, use passive analysis without side effects + - explicit: bool, prefer explicit import statements + + Returns: + str: likely import statement + """ +``` + +### Object Identification + +```python { .api } +def getname(obj, force=False, fqn=False): + """ + Get the name of an object. + + Attempts to determine the canonical name of an object by examining + its attributes, module context, and definition location. + + Parameters: + - obj: object to get name for + - force: bool, force name extraction even if not directly available + - fqn: bool, return fully qualified name including module path + + Returns: + str: object name or None if name cannot be determined + """ + +def isfrommain(obj): + """ + Check if object is from __main__ module. + + Determines whether an object originates from the main module, + which affects how it should be serialized and imported. + + Parameters: + - obj: object to check + + Returns: + bool: True if object is from __main__ module + """ + +def isdynamic(obj): + """ + Check if object is dynamically created. + + Determines whether an object was created dynamically at runtime + rather than being defined in source code. + + Parameters: + - obj: object to check + + Returns: + bool: True if object is dynamically created + """ +``` + +## Usage Examples + +### Basic Source Extraction + +```python +import dill.source as source + +# Define a function +def example_function(x, y=10): + """Example function with default parameter.""" + result = x + y + return result * 2 + +# Get source code +source_code = source.getsource(example_function) +print(source_code) +# Output: Full function definition as string + +# Get source lines with line numbers +lines, start_line = source.getsourcelines(example_function) +print(f"Function starts at line {start_line}") +for i, line in enumerate(lines): + print(f"{start_line + i}: {line.rstrip()}") +``` + +### Advanced Source Analysis + +```python +import dill.source as source + +class ExampleClass: + """Example class for source analysis.""" + + def __init__(self, value): + self.value = value + + def method(self, multiplier=2): + return self.value * multiplier + + @staticmethod + def static_method(x): + return x ** 2 + +# Analyze class source +class_source = source.getsource(ExampleClass) +print("Class source:") +print(class_source) + +# Get method source separately +method_source = source.getsource(ExampleClass.method) +print("\nMethod source:") +print(method_source) + +# Get source blocks +blocks = source.getblocks(ExampleClass) +print(f"\nFound {len(blocks)} code blocks in class") +``` + +### Import Generation + +```python +import dill.source as source +import json +from collections import defaultdict + +# Generate import statements for various objects +objects_to_import = [ + json.dumps, + defaultdict, + ExampleClass, + example_function +] + +for obj in objects_to_import: + try: + import_stmt = source.getimport(obj) + name = source.getname(obj) or str(obj) + print(f"{name}: {import_stmt}") + except Exception as e: + print(f"Could not generate import for {obj}: {e}") + +# Generate imports with aliases +alias_import = source.getimport(json.dumps, alias='json_serialize') +print(f"With alias: {alias_import}") +``` + +### Dynamic Code Analysis + +```python +import dill.source as source + +# Create dynamic function +exec(''' +def dynamic_function(x): + return x * 3 + 1 +''') + +# Analyze dynamic objects +if source.isdynamic(dynamic_function): + print("Function was created dynamically") + +if source.isfrommain(dynamic_function): + print("Function is from __main__ module") + +# Try to get source of dynamic function +try: + dynamic_source = source.getsource(dynamic_function) + print("Dynamic function source:") + print(dynamic_source) +except OSError: + print("Cannot get source for dynamic function") +``` + +### Source Code Modification + +```python +import dill.source as source + +def modify_function_source(func, modifications): + """Modify function source code.""" + try: + # Get original source + original_source = source.getsource(func) + + # Apply modifications + modified_source = original_source + for old, new in modifications.items(): + modified_source = modified_source.replace(old, new) + + # Create new function from modified source + namespace = {} + exec(modified_source, namespace) + + # Find the function in the namespace + func_name = source.getname(func) + if func_name in namespace: + return namespace[func_name] + + return None + + except Exception as e: + print(f"Failed to modify function: {e}") + return None + +# Example usage +def original_add(a, b): + return a + b + +# Modify to multiply instead +modifications = {'a + b': 'a * b'} +modified_func = modify_function_source(original_add, modifications) + +if modified_func: + print(f"Original: {original_add(3, 4)}") # 7 + print(f"Modified: {modified_func(3, 4)}") # 12 +``` + +## Advanced Features + +### Source Code Archival + +```python +import dill.source as source +import json + +def archive_source_code(objects, filename): + """Archive source code for multiple objects.""" + archive = {} + + for name, obj in objects.items(): + try: + obj_info = { + 'source': source.getsource(obj), + 'import': source.getimport(obj, verify=False), + 'name': source.getname(obj), + 'is_dynamic': source.isdynamic(obj), + 'is_from_main': source.isfrommain(obj) + } + archive[name] = obj_info + except Exception as e: + archive[name] = {'error': str(e)} + + with open(filename, 'w') as f: + json.dump(archive, f, indent=2) + + return archive + +# Archive important functions and classes +objects_to_archive = { + 'example_function': example_function, + 'ExampleClass': ExampleClass, + 'json_dumps': json.dumps +} + +archive = archive_source_code(objects_to_archive, 'source_archive.json') +print(f"Archived {len(archive)} objects") +``` + +### Code Dependency Analysis + +```python +import dill.source as source +import dill.detect as detect +import ast + +def analyze_dependencies(func): + """Analyze function dependencies.""" + try: + # Get source code + source_code = source.getsource(func) + + # Parse AST + tree = ast.parse(source_code) + + # Find imports and name references + dependencies = { + 'imports': [], + 'globals': [], + 'builtins': [] + } + + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + dependencies['imports'].append(alias.name) + elif isinstance(node, ast.ImportFrom): + module = node.module or '' + for alias in node.names: + dependencies['imports'].append(f"{module}.{alias.name}") + + # Get global variables referenced + global_vars = detect.referredglobals(func, recurse=True) + dependencies['globals'] = list(global_vars.keys()) + + return dependencies + + except Exception as e: + return {'error': str(e)} + +# Analyze function dependencies +deps = analyze_dependencies(example_function) +print("Function dependencies:") +for dep_type, items in deps.items(): + if items: + print(f" {dep_type}: {items}") +``` + +### Interactive Source Browser + +```python +import dill.source as source + +class SourceBrowser: + """Interactive source code browser.""" + + def __init__(self): + self.history = [] + + def browse(self, obj): + """Browse source code for an object.""" + try: + obj_name = source.getname(obj) or str(obj) + self.history.append(obj_name) + + print(f"\n{'='*60}") + print(f"Source for {obj_name}") + print(f"{'='*60}") + + # Show basic info + print(f"Type: {type(obj).__name__}") + print(f"From main: {source.isfrommain(obj)}") + print(f"Dynamic: {source.isdynamic(obj)}") + + # Show import statement + try: + import_stmt = source.getimport(obj, verify=False) + print(f"Import: {import_stmt}") + except: + print("Import: Not available") + + print(f"\nSource code:") + print("-" * 40) + + # Show source + source_code = source.getsource(obj) + print(source_code) + + except Exception as e: + print(f"Error browsing {obj}: {e}") + + def show_history(self): + """Show browsing history.""" + print("\nBrowsing history:") + for i, item in enumerate(self.history): + print(f" {i+1}. {item}") + +# Usage +browser = SourceBrowser() +browser.browse(example_function) +browser.browse(ExampleClass) +browser.show_history() +``` + +## Integration with Serialization + +### Source-Aware Pickling + +```python +import dill +import dill.source as source + +def pickle_with_source(obj, filename): + """Pickle object along with its source code.""" + # Create enhanced object with source + enhanced_obj = { + 'object': obj, + 'metadata': { + 'source': None, + 'import': None, + 'name': source.getname(obj), + 'type': type(obj).__name__ + } + } + + # Try to get source and import info + try: + enhanced_obj['metadata']['source'] = source.getsource(obj) + except: + pass + + try: + enhanced_obj['metadata']['import'] = source.getimport(obj, verify=False) + except: + pass + + # Pickle enhanced object + with open(filename, 'wb') as f: + dill.dump(enhanced_obj, f) + +def unpickle_with_source(filename): + """Unpickle object and display source information.""" + with open(filename, 'rb') as f: + enhanced_obj = dill.load(f) + + obj = enhanced_obj['object'] + metadata = enhanced_obj['metadata'] + + print(f"Loaded {metadata['type']}: {metadata['name']}") + + if metadata['import']: + print(f"Import: {metadata['import']}") + + if metadata['source']: + print("Source code:") + print(metadata['source']) + + return obj + +# Usage +pickle_with_source(example_function, 'function_with_source.pkl') +restored_func = unpickle_with_source('function_with_source.pkl') +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/temp-operations.md b/.tessl/tiles/tessl/pypi-dill/docs/temp-operations.md new file mode 100644 index 000000000..a4dccddef --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/temp-operations.md @@ -0,0 +1,705 @@ +# Temporary Operations + +dill.temp provides utilities for temporary file operations with serialization, stream capture, and IO buffer management for testing, development workflows, and temporary data handling. + +## File Operations + +### Temporary File Serialization + +```python { .api } +def dump(object, **kwds): + """ + Dump object to a NamedTemporaryFile using dill.dump. + + Serializes an object to a temporary file and returns the file handle, + useful for quick testing and temporary storage without managing filenames. + + Parameters: + - object: object to serialize to temporary file + - **kwds: optional keyword arguments including: + - suffix: str, file name suffix (default: no suffix) + - prefix: str, file name prefix (default: system default) + - other arguments passed to NamedTemporaryFile and dill.dump + + Returns: + file handle: NamedTemporaryFile handle containing serialized object + + Raises: + - PicklingError: when object cannot be serialized + - IOError: when temporary file operations fail + """ + +def load(file, **kwds): + """ + Load an object that was stored with dill.temp.dump. + + Deserializes an object from a file, typically used with temporary + files created by temp.dump but works with any dill-compatible file. + + Parameters: + - file: file handle or str, file handle or path to file containing serialized object + - **kwds: optional keyword arguments including: + - mode: str, file open mode ('r' or 'rb', default: 'rb') + - other arguments passed to open() and dill.load + + Returns: + object: deserialized object + + Raises: + - UnpicklingError: when object cannot be deserialized + - IOError: when file operations fail + """ +``` + +### Source Code File Operations + +```python { .api } +def load_source(file, **kwds): + """ + Load source code from file. + + Reads and executes Python source code from a file, returning + the executed namespace or specific objects. + + Parameters: + - file: str, path to Python source file + - **kwds: keyword arguments for source execution control + + Returns: + object: result of source code execution + + Raises: + - SyntaxError: when source code is invalid + - IOError: when file cannot be read + """ + +def dump_source(object, **kwds): + """ + Dump source code to temporary file. + + Extracts source code from an object and writes it to a temporary + file, useful for code analysis and temporary source storage. + + Parameters: + - object: object to extract source code from + - **kwds: keyword arguments for source extraction + + Returns: + str: path to temporary file containing source code + + Raises: + - OSError: when source code cannot be extracted + - IOError: when file operations fail + """ +``` + +## IO Buffer Operations + +### Buffer-based Serialization + +```python { .api } +def loadIO(buffer, **kwds): + """ + Load object from IO buffer. + + Deserializes an object from a bytes buffer or file-like object, + providing memory-based deserialization for testing and processing. + + Parameters: + - buffer: bytes or file-like object containing serialized data + - **kwds: keyword arguments passed to dill.load + + Returns: + object: deserialized object + + Raises: + - UnpicklingError: when deserialization fails + - TypeError: when buffer type is not supported + """ + +def dumpIO(object, **kwds): + """ + Dump object to IO buffer. + + Serializes an object to a bytes buffer, returning the buffer + for further processing or storage. + + Parameters: + - object: object to serialize + - **kwds: keyword arguments passed to dill.dumps + + Returns: + bytes: serialized object as bytes buffer + + Raises: + - PicklingError: when serialization fails + """ + +def loadIO_source(buffer, **kwds): + """ + Load source code from IO buffer. + + Reads and executes Python source code from a buffer, + useful for dynamic code execution and testing. + + Parameters: + - buffer: str or bytes containing Python source code + - **kwds: keyword arguments for execution control + + Returns: + object: result of source code execution + + Raises: + - SyntaxError: when source code is invalid + """ + +def dumpIO_source(object, **kwds): + """ + Dump source code to IO buffer. + + Extracts source code from an object and returns it as a string buffer, + useful for in-memory source code processing. + + Parameters: + - object: object to extract source code from + - **kwds: keyword arguments for source extraction + + Returns: + str: source code as string buffer + + Raises: + - OSError: when source code cannot be extracted + """ +``` + +## Stream Capture + +### Output Capture Utilities + +```python { .api } +def capture(stream='stdout'): + """ + Capture stdout or stderr stream. + + Context manager that captures output from stdout or stderr, + useful for testing, logging, and output analysis. + + Parameters: + - stream: str, stream name ('stdout' or 'stderr') + + Returns: + context manager that captures stream output + + Usage: + with dill.temp.capture('stdout') as output: + print("Hello") + captured_text = output.getvalue() + """ +``` + +## Usage Examples + +### Basic Temporary File Operations + +```python +import dill.temp as temp + +# Create some test data +def test_function(x, y): + return x * y + 42 + +class TestClass: + def __init__(self, value): + self.value = value + + def get_doubled(self): + return self.value * 2 + +test_data = { + 'function': test_function, + 'class': TestClass, + 'instance': TestClass(10), + 'list': [1, 2, 3, 4, 5] +} + +# Dump to temporary file +temp_file = temp.dump(test_data) +print(f"Data saved to temporary file: {temp_file}") + +# Load from temporary file +restored_data = temp.load(temp_file) +print(f"Restored data keys: {list(restored_data.keys())}") + +# Test restored functionality +restored_function = restored_data['function'] +result = restored_function(5, 3) +print(f"Function result: {result}") # Should be 57 + +restored_instance = restored_data['instance'] +doubled = restored_instance.get_doubled() +print(f"Instance method result: {doubled}") # Should be 20 +``` + +### Source Code Operations + +```python +import dill.temp as temp + +# Define a function to work with +def example_algorithm(data, threshold=0.5): + """Example algorithm for demonstration.""" + processed = [] + for item in data: + if isinstance(item, (int, float)) and item > threshold: + processed.append(item * 2) + else: + processed.append(item) + return processed + +# Dump source code to temporary file +source_file = temp.dump_source(example_algorithm) +print(f"Source code saved to: {source_file}") + +# Load and examine source code +with open(source_file, 'r') as f: + source_content = f.read() + print("Source code:") + print(source_content) + +# Load source code as executable +loaded_func = temp.load_source(source_file) +print(f"Loaded function: {loaded_func}") + +# Test loaded function +test_data = [0.2, 0.8, 1.5, 0.1, 2.0] +result = loaded_func(test_data, threshold=0.6) +print(f"Algorithm result: {result}") +``` + +### IO Buffer Operations + +```python +import dill.temp as temp +import io + +# Complex object for testing +complex_data = { + 'functions': [lambda x: x**2, lambda x: x**3], + 'nested': {'level1': {'level2': [1, 2, 3]}}, + 'instance': TestClass(42) +} + +# Serialize to buffer +buffer_data = temp.dumpIO(complex_data) +print(f"Serialized to buffer: {len(buffer_data)} bytes") + +# Deserialize from buffer +restored_from_buffer = temp.loadIO(buffer_data) +print("Restored from buffer successfully") + +# Test functionality +square_func = restored_from_buffer['functions'][0] +print(f"Square function: {square_func(5)}") # Should be 25 + +# Source code buffer operations +source_buffer = temp.dumpIO_source(example_algorithm) +print(f"Source code buffer length: {len(source_buffer)} characters") + +# Load source from buffer +namespace = {} +exec(source_buffer, namespace) +func_from_buffer = namespace['example_algorithm'] +result = func_from_buffer([1, 2, 3], threshold=1.5) +print(f"Function from source buffer: {result}") +``` + +### Stream Capture + +```python +import dill.temp as temp + +def noisy_function(): + """Function that produces output.""" + print("Starting processing...") + print("Processing item 1") + print("Processing item 2") + print("Processing complete!") + return "Result" + +def error_function(): + """Function that produces error output.""" + import sys + print("This goes to stdout") + print("This goes to stderr", file=sys.stderr) + return "Done" + +# Capture stdout +with temp.capture('stdout') as stdout_capture: + result = noisy_function() + +stdout_output = stdout_capture.getvalue() +print(f"Captured stdout ({len(stdout_output)} chars):") +print(repr(stdout_output)) + +# Capture stderr +with temp.capture('stderr') as stderr_capture: + result = error_function() + +stderr_output = stderr_capture.getvalue() +print(f"Captured stderr: {repr(stderr_output)}") + +# Capture both streams +import sys +from contextlib import redirect_stdout, redirect_stderr + +def capture_both_streams(func): + """Capture both stdout and stderr.""" + stdout_buffer = io.StringIO() + stderr_buffer = io.StringIO() + + with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer): + result = func() + + return { + 'result': result, + 'stdout': stdout_buffer.getvalue(), + 'stderr': stderr_buffer.getvalue() + } + +# Usage +captured = capture_both_streams(error_function) +print(f"Function result: {captured['result']}") +print(f"Stdout: {repr(captured['stdout'])}") +print(f"Stderr: {repr(captured['stderr'])}") +``` + +## Advanced Use Cases + +### Testing Framework Integration + +```python +import dill.temp as temp +import unittest +import os + +class TempFileTestCase(unittest.TestCase): + """Test case with temporary file management.""" + + def setUp(self): + self.temp_files = [] + + def tearDown(self): + # Clean up temporary files + for temp_file in self.temp_files: + try: + if os.path.exists(temp_file): + os.remove(temp_file) + except: + pass + + def create_temp_object(self, obj): + """Create temporary file for object and track it.""" + temp_file = temp.dump(obj) + self.temp_files.append(temp_file) + return temp_file + + def test_function_serialization(self): + """Test function serialization via temporary files.""" + def test_func(x): + return x * 2 + + # Serialize to temp file + temp_file = self.create_temp_object(test_func) + self.assertTrue(os.path.exists(temp_file)) + + # Load and test + loaded_func = temp.load(temp_file) + self.assertEqual(loaded_func(5), 10) + + def test_complex_object_roundtrip(self): + """Test complex object serialization roundtrip.""" + complex_obj = { + 'data': [1, 2, 3, 4, 5], + 'func': lambda x: sum(x), + 'nested': {'key': 'value'} + } + + temp_file = self.create_temp_object(complex_obj) + loaded_obj = temp.load(temp_file) + + # Test loaded functionality + self.assertEqual(loaded_obj['data'], [1, 2, 3, 4, 5]) + self.assertEqual(loaded_obj['func']([1, 2, 3]), 6) + self.assertEqual(loaded_obj['nested']['key'], 'value') + +# Run tests +if __name__ == '__main__': + unittest.main() +``` + +### Development Workflow Tools + +```python +import dill.temp as temp +import datetime +import json + +class DevelopmentSession: + """Manage development session with temporary serialization.""" + + def __init__(self, session_name): + self.session_name = session_name + self.snapshots = [] + self.current_objects = {} + + def add_object(self, name, obj): + """Add object to current session.""" + self.current_objects[name] = obj + print(f"Added {name} to session") + + def take_snapshot(self, description=""): + """Take snapshot of current session state.""" + timestamp = datetime.datetime.now().isoformat() + + # Serialize current objects to temporary file + temp_file = temp.dump(self.current_objects) + + snapshot = { + 'timestamp': timestamp, + 'description': description, + 'temp_file': temp_file, + 'object_count': len(self.current_objects) + } + + self.snapshots.append(snapshot) + print(f"Snapshot taken: {len(self.snapshots)} total snapshots") + return len(self.snapshots) - 1 + + def restore_snapshot(self, snapshot_index): + """Restore session from snapshot.""" + if 0 <= snapshot_index < len(self.snapshots): + snapshot = self.snapshots[snapshot_index] + + # Load objects from temporary file + self.current_objects = temp.load(snapshot['temp_file']) + + print(f"Restored snapshot from {snapshot['timestamp']}") + print(f"Objects restored: {list(self.current_objects.keys())}") + else: + print(f"Invalid snapshot index: {snapshot_index}") + + def list_snapshots(self): + """List all snapshots.""" + for i, snapshot in enumerate(self.snapshots): + print(f"{i}: {snapshot['timestamp']} - {snapshot['description']} ({snapshot['object_count']} objects)") + + def get_object(self, name): + """Get object from current session.""" + return self.current_objects.get(name) + + def export_session_info(self, filename): + """Export session metadata to JSON.""" + session_info = { + 'session_name': self.session_name, + 'snapshots': [ + { + 'timestamp': s['timestamp'], + 'description': s['description'], + 'object_count': s['object_count'] + } + for s in self.snapshots + ], + 'current_objects': list(self.current_objects.keys()) + } + + with open(filename, 'w') as f: + json.dump(session_info, f, indent=2) + + print(f"Session info exported to {filename}") + +# Usage example +session = DevelopmentSession("algorithm_development") + +# Add some objects to work with +def fibonacci(n): + if n <= 1: + return n + return fibonacci(n-1) + fibonacci(n-2) + +class DataProcessor: + def __init__(self, multiplier=1): + self.multiplier = multiplier + + def process(self, data): + return [x * self.multiplier for x in data] + +session.add_object('fib_func', fibonacci) +session.add_object('processor', DataProcessor(2)) +session.add_object('test_data', [1, 2, 3, 4, 5]) + +# Take initial snapshot +session.take_snapshot("Initial development state") + +# Modify objects +session.add_object('processor', DataProcessor(3)) # Change multiplier +session.add_object('results', session.get_object('processor').process(session.get_object('test_data'))) + +# Take another snapshot +session.take_snapshot("After modifications") + +# Show snapshots +session.list_snapshots() + +# Restore previous state +session.restore_snapshot(0) + +# Export session info +session.export_session_info('dev_session.json') +``` + +### Performance Benchmarking + +```python +import dill.temp as temp +import time +import sys + +class SerializationBenchmark: + """Benchmark serialization performance with temporary operations.""" + + def __init__(self): + self.results = {} + + def benchmark_temp_operations(self, test_objects, iterations=10): + """Benchmark temporary file operations.""" + for name, obj in test_objects.items(): + print(f"Benchmarking {name}...") + + # Measure temp.dump performance + dump_times = [] + load_times = [] + file_sizes = [] + + for i in range(iterations): + # Time dump operation + start_time = time.time() + temp_file = temp.dump(obj) + dump_time = time.time() - start_time + dump_times.append(dump_time) + + # Get file size + file_size = os.path.getsize(temp_file) + file_sizes.append(file_size) + + # Time load operation + start_time = time.time() + temp.load(temp_file) + load_time = time.time() - start_time + load_times.append(load_time) + + # Clean up + os.remove(temp_file) + + self.results[name] = { + 'avg_dump_time': sum(dump_times) / len(dump_times), + 'avg_load_time': sum(load_times) / len(load_times), + 'avg_file_size': sum(file_sizes) / len(file_sizes), + 'min_dump_time': min(dump_times), + 'max_dump_time': max(dump_times), + 'min_load_time': min(load_times), + 'max_load_time': max(load_times) + } + + def benchmark_io_operations(self, test_objects, iterations=10): + """Benchmark IO buffer operations.""" + for name, obj in test_objects.items(): + print(f"Benchmarking IO operations for {name}...") + + dump_times = [] + load_times = [] + buffer_sizes = [] + + for i in range(iterations): + # Time dumpIO operation + start_time = time.time() + buffer_data = temp.dumpIO(obj) + dump_time = time.time() - start_time + dump_times.append(dump_time) + + buffer_sizes.append(len(buffer_data)) + + # Time loadIO operation + start_time = time.time() + temp.loadIO(buffer_data) + load_time = time.time() - start_time + load_times.append(load_time) + + io_results = { + 'avg_dump_time': sum(dump_times) / len(dump_times), + 'avg_load_time': sum(load_times) / len(load_times), + 'avg_buffer_size': sum(buffer_sizes) / len(buffer_sizes) + } + + if name in self.results: + self.results[name]['io'] = io_results + else: + self.results[name] = {'io': io_results} + + def print_results(self): + """Print benchmark results.""" + print("\\nSerialization Benchmark Results") + print("=" * 60) + + for name, results in self.results.items(): + print(f"\\n{name}:") + print("-" * 30) + + if 'avg_dump_time' in results: # File operations + print(f"File Operations:") + print(f" Avg dump time: {results['avg_dump_time']:.4f}s") + print(f" Avg load time: {results['avg_load_time']:.4f}s") + print(f" Avg file size: {results['avg_file_size']:,.0f} bytes") + + if 'io' in results: # IO operations + io = results['io'] + print(f"IO Buffer Operations:") + print(f" Avg dump time: {io['avg_dump_time']:.4f}s") + print(f" Avg load time: {io['avg_load_time']:.4f}s") + print(f" Avg buffer size: {io['avg_buffer_size']:,.0f} bytes") + +# Run benchmark +benchmark = SerializationBenchmark() + +# Create test objects of varying complexity +test_objects = { + 'simple_list': list(range(1000)), + 'function': lambda x: x**2 + x + 1, + 'class_instance': TestClass(100), + 'nested_dict': {'level1': {'level2': {'level3': list(range(100))}}}, + 'mixed_structure': { + 'data': list(range(500)), + 'funcs': [lambda x: x+1, lambda x: x*2], + 'objects': [TestClass(i) for i in range(10)] + } +} + +benchmark.benchmark_temp_operations(test_objects, iterations=5) +benchmark.benchmark_io_operations(test_objects, iterations=5) +benchmark.print_results() +``` + +## Best Practices + +### Temporary File Management + +1. **Cleanup**: Always clean up temporary files when done +2. **Path Management**: Store temp file paths for later cleanup +3. **Error Handling**: Handle file operation errors gracefully +4. **Security**: Be aware of temporary file permissions and location + +### Performance Optimization + +1. **Buffer vs File**: Use IO buffers for small objects, files for large ones +2. **Memory Usage**: Monitor memory usage with large temporary operations +3. **Disk Space**: Monitor disk space usage with temporary files +4. **Cleanup Frequency**: Clean up temporary files regularly in long-running processes \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/type-registry.md b/.tessl/tiles/tessl/pypi-dill/docs/type-registry.md new file mode 100644 index 000000000..4e12640a1 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/docs/type-registry.md @@ -0,0 +1,397 @@ +# Type Registry System + +dill provides a flexible type registry system that allows registration of custom types and extension of serialization capabilities to handle new object types that are not supported by default. + +## Core Registry Functions + +### Type Registration + +```python { .api } +def register(t): + """ + Register a type with the pickler. + + Decorator function that registers a custom pickling function for a specific + type, allowing dill to handle objects of that type during serialization. + + Parameters: + - t: type, the type to register for custom pickling + + Returns: + function: decorator function that takes the pickling function + + Usage: + @dill.register(MyCustomType) + def save_custom_type(pickler, obj): + # Custom pickling logic + pass + """ + +def pickle(t, func): + """ + Add a type to the pickle dispatch table. + + Directly associates a type with a pickling function in the dispatch table, + enabling automatic handling of objects of that type. + + Parameters: + - t: type, the type to add to dispatch table + - func: function, pickling function that handles objects of type t + + Returns: + None + + Raises: + - TypeError: when type or function parameters are invalid + """ + +def extend(use_dill=True): + """ + Add or remove dill types to/from the pickle registry. + + Controls whether dill's extended types are available in the standard + pickle module's dispatch table, allowing integration with existing code. + + Parameters: + - use_dill: bool, if True extend dispatch table with dill types, + if False revert to standard pickle types only + + Returns: + None + """ +``` + +### Registry Inspection + +```python { .api } +def dispatch_table(): + """ + Get the current dispatch table. + + Returns the current pickle dispatch table showing all registered + types and their associated pickling functions. + + Returns: + dict: mapping of types to pickling functions + """ +``` + +## Usage Examples + +### Basic Type Registration + +```python +import dill + +# Define a custom class +class CustomData: + def __init__(self, data, metadata=None): + self.data = data + self.metadata = metadata or {} + + def __repr__(self): + return f"CustomData({self.data}, {self.metadata})" + +# Register custom pickling function using decorator +@dill.register(CustomData) +def save_custom_data(pickler, obj): + """Custom pickling function for CustomData objects.""" + # Save the class reference + pickler.save_reduce(_restore_custom_data, + (obj.data, obj.metadata), obj=obj) + +def _restore_custom_data(data, metadata): + """Helper function to restore CustomData objects.""" + return CustomData(data, metadata) + +# Test the registration +custom_obj = CustomData([1, 2, 3], {"version": "1.0"}) + +# Now it can be pickled +serialized = dill.dumps(custom_obj) +restored = dill.loads(serialized) + +print(f"Original: {custom_obj}") +print(f"Restored: {restored}") +``` + +### Direct Dispatch Table Modification + +```python +import dill + +class SpecialContainer: + def __init__(self, items): + self.items = list(items) + + def __eq__(self, other): + return isinstance(other, SpecialContainer) and self.items == other.items + +def pickle_special_container(pickler, obj): + """Pickling function for SpecialContainer.""" + # Use pickle protocol to save class and items + pickler.save_reduce(SpecialContainer, (obj.items,), obj=obj) + +# Add to dispatch table directly +dill.pickle(SpecialContainer, pickle_special_container) + +# Test the registration +container = SpecialContainer([1, 2, 3, 4]) +pickled_data = dill.dumps(container) +unpickled_container = dill.loads(pickled_data) + +print(f"Original == Restored: {container == unpickled_container}") +``` + +### Complex Type Registration + +```python +import dill +import weakref + +class Node: + """Tree node with parent/child relationships.""" + def __init__(self, value, parent=None): + self.value = value + self.children = [] + self._parent_ref = None + if parent: + self.set_parent(parent) + + def set_parent(self, parent): + if self._parent_ref: + old_parent = self._parent_ref() + if old_parent: + old_parent.children.remove(self) + + self._parent_ref = weakref.ref(parent) if parent else None + if parent: + parent.children.append(self) + + @property + def parent(self): + return self._parent_ref() if self._parent_ref else None + +# Register complex type with custom handling +@dill.register(Node) +def save_node(pickler, obj): + """Custom pickling for Node objects.""" + # Save value and children (parent will be reconstructed) + pickler.save_reduce(_restore_node, + (obj.value, obj.children), obj=obj) + +def _restore_node(value, children): + """Restore Node with proper parent/child relationships.""" + node = Node(value) + + # Restore children and set parent relationships + for child in children: + if isinstance(child, Node): + child.set_parent(node) + else: + # Handle case where child was also pickled/unpickled + node.children.append(child) + + return node + +# Test complex type registration +root = Node("root") +child1 = Node("child1", root) +child2 = Node("child2", root) +grandchild = Node("grandchild", child1) + +# Pickle the entire tree +tree_data = dill.dumps(root) +restored_root = dill.loads(tree_data) + +print(f"Root children: {[child.value for child in restored_root.children]}") +print(f"Child1 parent: {restored_root.children[0].parent.value}") +``` + +### Registry Management + +```python +import dill + +class RegistryManager: + """Manage custom type registrations.""" + + def __init__(self): + self.registered_types = {} + self.original_dispatch = None + + def register_type(self, type_class, save_func, restore_func=None): + """Register a type with save and optional restore functions.""" + if restore_func: + # Store restore function for later use + self.registered_types[type_class] = { + 'save': save_func, + 'restore': restore_func + } + + # Create wrapper that includes restore function + def save_wrapper(pickler, obj): + pickler.save_reduce(restore_func, save_func(obj), obj=obj) + + dill.register(type_class)(save_wrapper) + else: + # Direct registration + dill.register(type_class)(save_func) + self.registered_types[type_class] = {'save': save_func} + + def unregister_type(self, type_class): + """Remove type from registry.""" + if type_class in self.registered_types: + # Remove from dill's dispatch table + dispatch = dill.dispatch_table() + if type_class in dispatch: + del dispatch[type_class] + + del self.registered_types[type_class] + + def list_registered_types(self): + """List all registered custom types.""" + return list(self.registered_types.keys()) + + def backup_dispatch_table(self): + """Backup current dispatch table.""" + self.original_dispatch = dill.dispatch_table().copy() + + def restore_dispatch_table(self): + \"\"\"Restore original dispatch table.\"\"\" + if self.original_dispatch: + current_dispatch = dill.dispatch_table() + current_dispatch.clear() + current_dispatch.update(self.original_dispatch) + +# Usage example +registry = RegistryManager() + +# Backup original state +registry.backup_dispatch_table() + +# Register multiple types +registry.register_type( + CustomData, + lambda obj: (obj.data, obj.metadata), + lambda data, meta: CustomData(data, meta) +) + +registry.register_type( + SpecialContainer, + lambda obj: (obj.items,), + lambda items: SpecialContainer(items) +) + +print(f"Registered types: {[t.__name__ for t in registry.list_registered_types()]}") + +# Test registrations +test_objects = [ + CustomData([1, 2, 3], {"test": True}), + SpecialContainer(["a", "b", "c"]) +] + +for obj in test_objects: + try: + data = dill.dumps(obj) + restored = dill.loads(data) + print(f"✓ {type(obj).__name__}: Successfully pickled and restored") + except Exception as e: + print(f"✗ {type(obj).__name__}: Failed - {e}") + +# Clean up +registry.restore_dispatch_table() +``` + +## Advanced Registry Techniques + +### Conditional Type Registration + +```python +import dill +import sys + +class ConditionalRegistry: + """Register types based on conditions.""" + + @staticmethod + def register_if_available(type_name, module_name, save_func, restore_func=None): + """Register type only if module is available.""" + try: + module = __import__(module_name) + type_class = getattr(module, type_name) + + if restore_func: + def save_wrapper(pickler, obj): + pickler.save_reduce(restore_func, save_func(obj), obj=obj) + dill.register(type_class)(save_wrapper) + else: + dill.register(type_class)(save_func) + + print(f"✓ Registered {type_name} from {module_name}") + return True + + except (ImportError, AttributeError) as e: + print(f"✗ Could not register {type_name}: {e}") + return False + + @staticmethod + def register_for_python_version(min_version, type_class, save_func): + """Register type only for specific Python versions.""" + if sys.version_info >= min_version: + dill.register(type_class)(save_func) + print(f"✓ Registered {type_class.__name__} for Python {sys.version_info[:2]}") + return True + else: + print(f"✗ Skipped {type_class.__name__} - requires Python {min_version}") + return False + +# Example usage +registry = ConditionalRegistry() + +# Register numpy array if numpy is available +registry.register_if_available( + 'ndarray', 'numpy', + lambda obj: (obj.tobytes(), obj.dtype, obj.shape), + lambda data, dtype, shape: __import__('numpy').frombuffer(data, dtype).reshape(shape) +) + +# Register pathlib.Path for Python 3.4+ +import pathlib +registry.register_for_python_version( + (3, 4), + pathlib.Path, + lambda obj: str(obj) +) +``` + +### Dynamic Type Discovery + +```python +import dill +import inspect + +class AutoRegistry: + """Automatically discover and register types.""" + + def __init__(self): + self.auto_registered = set() + + def auto_register_module(self, module, save_pattern=None, exclude=None): + """Automatically register types from a module.""" + exclude = exclude or [] + registered_count = 0 + + for name, obj in inspect.getmembers(module): + # Skip private and excluded items + if name.startswith('_') or name in exclude: + continue + + # Only register classes + if inspect.isclass(obj): + # Check if we can create a default save function + if self._can_auto_register(obj): + self._auto_register_type(obj) + registered_count += 1 + + print(f\"Auto-registered {registered_count} types from {module.__name__}\")\n return registered_count\n \n def _can_auto_register(self, type_class):\n \"\"\"Check if type can be auto-registered.\"\"\"\n try:\n # Check if type has __dict__ or __slots__\n if hasattr(type_class, '__dict__') or hasattr(type_class, '__slots__'):\n return True\n \n # Check if it's a simple dataclass-like structure\n if hasattr(type_class, '__init__'):\n sig = inspect.signature(type_class.__init__)\n # Simple heuristic: if all parameters have defaults or annotations\n return len(sig.parameters) <= 5 # Reasonable limit\n \n except Exception:\n pass\n \n return False\n \n def _auto_register_type(self, type_class):\n \"\"\"Automatically register a type with default behavior.\"\"\"\n if type_class in self.auto_registered:\n return\n \n def auto_save_func(pickler, obj):\n # Generic save function using __dict__ or __getstate__\n if hasattr(obj, '__getstate__'):\n state = obj.__getstate__()\n pickler.save_reduce(_auto_restore_with_state, \n (type_class, state), obj=obj)\n elif hasattr(obj, '__dict__'):\n pickler.save_reduce(_auto_restore_with_dict,\n (type_class, obj.__dict__), obj=obj)\n else:\n # Fallback to trying __reduce__\n pickler.save_reduce(type_class, (), obj=obj)\n \n dill.register(type_class)(auto_save_func)\n self.auto_registered.add(type_class)\n print(f\"Auto-registered {type_class.__name__}\")\n\ndef _auto_restore_with_state(type_class, state):\n \"\"\"Restore object using __setstate__.\"\"\"\n obj = type_class.__new__(type_class)\n if hasattr(obj, '__setstate__'):\n obj.__setstate__(state)\n else:\n obj.__dict__.update(state)\n return obj\n\ndef _auto_restore_with_dict(type_class, obj_dict):\n \"\"\"Restore object using __dict__.\"\"\"\n obj = type_class.__new__(type_class)\n obj.__dict__.update(obj_dict)\n return obj\n\n# Example usage\nauto_registry = AutoRegistry()\n\n# Auto-register types from a custom module\n# auto_registry.auto_register_module(my_custom_module, exclude=['BaseClass'])\n```\n\n## Integration with Existing Code\n\n### Backward Compatibility\n\n```python\nimport dill\nimport pickle\n\nclass CompatibilityManager:\n \"\"\"Manage compatibility between dill and standard pickle.\"\"\"\n \n def __init__(self):\n self.dill_enabled = True\n \n def enable_dill_types(self):\n \"\"\"Enable dill types in pickle dispatch table.\"\"\"\n dill.extend(use_dill=True)\n self.dill_enabled = True\n print(\"Dill types enabled in pickle\")\n \n def disable_dill_types(self):\n \"\"\"Disable dill types, use only standard pickle.\"\"\"\n dill.extend(use_dill=False)\n self.dill_enabled = False\n print(\"Dill types disabled, using standard pickle only\")\n \n def test_compatibility(self, test_objects):\n \"\"\"Test objects with both pickle and dill.\"\"\"\n results = {}\n \n for name, obj in test_objects.items():\n results[name] = {\n 'dill': self._test_with_dill(obj),\n 'pickle': self._test_with_pickle(obj)\n }\n \n return results\n \n def _test_with_dill(self, obj):\n try:\n data = dill.dumps(obj)\n dill.loads(data)\n return \"✓ Success\"\n except Exception as e:\n return f\"✗ Failed: {e}\"\n \n def _test_with_pickle(self, obj):\n try:\n data = pickle.dumps(obj)\n pickle.loads(data)\n return \"✓ Success\"\n except Exception as e:\n return f\"✗ Failed: {e}\"\n\n# Usage\ncompat = CompatibilityManager()\n\n# Test various objects\ntest_objects = {\n 'function': lambda x: x + 1,\n 'class': CustomData,\n 'instance': CustomData([1, 2, 3]),\n 'list': [1, 2, 3, 4, 5]\n}\n\nprint(\"Testing with dill types enabled:\")\ncompat.enable_dill_types()\nresults_enabled = compat.test_compatibility(test_objects)\n\nprint(\"\\nTesting with dill types disabled:\")\ncompat.disable_dill_types()\nresults_disabled = compat.test_compatibility(test_objects)\n\n# Compare results\nprint(\"\\nCompatibility Results:\")\nprint(\"-\" * 50)\nfor name in test_objects:\n print(f\"{name}:\")\n print(f\" Dill enabled - pickle: {results_enabled[name]['pickle']}\")\n print(f\" Dill disabled - pickle: {results_disabled[name]['pickle']}\")\n print(f\" Dill enabled - dill: {results_enabled[name]['dill']}\")\n\n# Re-enable for normal operation\ncompat.enable_dill_types()\n```\n\n## Best Practices\n\n### Registry Guidelines\n\n1. **Performance**: Keep pickling functions simple and fast\n2. **Robustness**: Handle edge cases and provide error recovery\n3. **Compatibility**: Test with different Python versions and environments\n4. **Documentation**: Document custom types and their pickling behavior\n5. **Testing**: Verify that pickled objects restore correctly with full functionality\n\n### Error Handling\n\n```python\nimport dill\nimport logging\n\nclass RobustRegistry:\n \"\"\"Registry with comprehensive error handling.\"\"\"\n \n def __init__(self):\n self.logger = logging.getLogger(__name__)\n \n def safe_register(self, type_class, save_func, restore_func=None):\n \"\"\"Register type with error handling.\"\"\"\n try:\n # Validate inputs\n if not isinstance(type_class, type):\n raise TypeError(\"type_class must be a type\")\n \n if not callable(save_func):\n raise TypeError(\"save_func must be callable\")\n \n if restore_func and not callable(restore_func):\n raise TypeError(\"restore_func must be callable\")\n \n # Test the registration with a dummy object\n self._test_registration(type_class, save_func, restore_func)\n \n # Perform actual registration\n if restore_func:\n def wrapper(pickler, obj):\n try:\n args = save_func(obj)\n pickler.save_reduce(restore_func, args, obj=obj)\n except Exception as e:\n self.logger.error(f\"Error saving {type_class.__name__}: {e}\")\n raise\n \n dill.register(type_class)(wrapper)\n else:\n dill.register(type_class)(save_func)\n \n self.logger.info(f\"Successfully registered {type_class.__name__}\")\n return True\n \n except Exception as e:\n self.logger.error(f\"Failed to register {type_class.__name__}: {e}\")\n return False\n \n def _test_registration(self, type_class, save_func, restore_func):\n \"\"\"Test registration with dummy data.\"\"\"\n # This would need to be implemented based on specific type requirements\n pass\n\n# Usage\nrobust_registry = RobustRegistry()\nlogging.basicConfig(level=logging.INFO)\n\nsuccess = robust_registry.safe_register(\n CustomData,\n lambda obj: (obj.data, obj.metadata),\n lambda data, meta: CustomData(data, meta)\n)\n\nif success:\n print(\"Registration successful\")\nelse:\n print(\"Registration failed - check logs\")\n``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/tile.json b/.tessl/tiles/tessl/pypi-dill/tile.json new file mode 100644 index 000000000..f3c94b616 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-dill/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-dill", + "version": "0.4.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/dill@0.4.0", + "summary": "Extended Python serialization library that can serialize functions, classes, and complex objects that standard pickle cannot handle." +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/batches.md b/.tessl/tiles/tessl/pypi-google-genai/docs/batches.md new file mode 100644 index 000000000..1c9fc206f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/batches.md @@ -0,0 +1,408 @@ +# Batch Processing + +Submit batch prediction jobs for high-volume inference with cost savings. Batch processing allows you to process large numbers of requests asynchronously at reduced costs compared to online inference, ideal for processing datasets, bulk content generation, and embeddings. + +## Capabilities + +### Create Batch Job + +Create a batch prediction job for content generation. Processes requests from Cloud Storage or BigQuery and writes results back. + +```python { .api } +class Batches: + """Synchronous batch prediction jobs API.""" + + def create( + self, + *, + model: str, + src: Union[str, list[dict]], + dest: Optional[str] = None, + config: Optional[CreateBatchJobConfig] = None + ) -> BatchJob: + """ + Create batch prediction job for content generation. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash'). + src (Union[str, list[dict]]): Source of requests. Can be: + - str: GCS URI ('gs://bucket/input.jsonl') or BigQuery table + ('bq://project.dataset.table') + - list[dict]: List of request dictionaries for inline requests + dest (str, optional): Destination for results. GCS URI ('gs://bucket/output') + or BigQuery table ('bq://project.dataset.table'). Required for GCS/BigQuery + sources, optional for inline requests. + config (CreateBatchJobConfig, optional): Job configuration. + + Returns: + BatchJob: Created batch job with name and status. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncBatches: + """Asynchronous batch prediction jobs API.""" + + async def create( + self, + *, + model: str, + src: Union[str, list[dict]], + dest: Optional[str] = None, + config: Optional[CreateBatchJobConfig] = None + ) -> BatchJob: + """Async version of create.""" + ... +``` + +**Usage Example - GCS:** + +```python +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Create batch job from GCS +job = client.batches.create( + model='gemini-2.0-flash', + src='gs://my-bucket/requests.jsonl', + dest='gs://my-bucket/results/' +) + +print(f"Job created: {job.name}") +print(f"State: {job.state}") + +# Poll for completion +import time +while job.state in ['JOB_STATE_PENDING', 'JOB_STATE_RUNNING']: + time.sleep(60) + job = client.batches.get(name=job.name) + print(f"State: {job.state}") + +print(f"Final state: {job.state}") +if job.state == 'JOB_STATE_SUCCEEDED': + print(f"Processed: {job.metadata.completed_requests} requests") +``` + +**Usage Example - Inline Requests:** + +```python +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Create batch job with inline requests +requests = [ + {'contents': 'What is AI?'}, + {'contents': 'Explain machine learning'}, + {'contents': 'What is deep learning?'} +] + +job = client.batches.create( + model='gemini-2.0-flash', + src=requests, + dest='gs://my-bucket/inline-results/' +) + +print(f"Job: {job.name}") +``` + +### Create Embeddings Batch Job + +Create a batch job specifically for embedding generation, optimized for generating embeddings for large datasets. + +```python { .api } +class Batches: + """Synchronous batch prediction jobs API.""" + + def create_embeddings( + self, + *, + model: str, + src: Union[str, list[dict]], + dest: Optional[str] = None, + config: Optional[CreateBatchJobConfig] = None + ) -> BatchJob: + """ + Create batch embeddings job. + + Parameters: + model (str): Embedding model (e.g., 'text-embedding-004'). + src (Union[str, list[dict]]): Source of embedding requests. + dest (str, optional): Destination for embeddings. + config (CreateBatchJobConfig, optional): Job configuration. + + Returns: + BatchJob: Created batch embeddings job. + """ + ... + +class AsyncBatches: + """Asynchronous batch prediction jobs API.""" + + async def create_embeddings( + self, + *, + model: str, + src: Union[str, list[dict]], + dest: Optional[str] = None, + config: Optional[CreateBatchJobConfig] = None + ) -> BatchJob: + """Async version of create_embeddings.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +job = client.batches.create_embeddings( + model='text-embedding-004', + src='gs://my-bucket/texts.jsonl', + dest='gs://my-bucket/embeddings/' +) + +print(f"Embeddings job: {job.name}") +``` + +### Get Batch Job + +Retrieve information about a batch job including status and progress. + +```python { .api } +class Batches: + """Synchronous batch prediction jobs API.""" + + def get(self, *, name: str) -> BatchJob: + """ + Get batch job status and information. + + Parameters: + name (str): Job name in format 'projects/*/locations/*/batchPredictionJobs/*'. + + Returns: + BatchJob: Job information including state, progress, and metadata. + """ + ... + +class AsyncBatches: + """Asynchronous batch prediction jobs API.""" + + async def get(self, *, name: str) -> BatchJob: + """Async version of get.""" + ... +``` + +### Cancel Batch Job + +Cancel a running batch job. + +```python { .api } +class Batches: + """Synchronous batch prediction jobs API.""" + + def cancel(self, *, name: str) -> None: + """ + Cancel a batch job. + + Parameters: + name (str): Job name. + """ + ... + +class AsyncBatches: + """Asynchronous batch prediction jobs API.""" + + async def cancel(self, *, name: str) -> None: + """Async version of cancel.""" + ... +``` + +### Delete Batch Job + +Delete a batch job. + +```python { .api } +class Batches: + """Synchronous batch prediction jobs API.""" + + def delete(self, *, name: str) -> None: + """ + Delete a batch job. + + Parameters: + name (str): Job name. + """ + ... + +class AsyncBatches: + """Asynchronous batch prediction jobs API.""" + + async def delete(self, *, name: str) -> None: + """Async version of delete.""" + ... +``` + +### List Batch Jobs + +List all batch jobs with optional filtering and pagination. + +```python { .api } +class Batches: + """Synchronous batch prediction jobs API.""" + + def list( + self, + *, + config: Optional[ListBatchJobsConfig] = None + ) -> Union[Pager[BatchJob], Iterator[BatchJob]]: + """ + List batch jobs. + + Parameters: + config (ListBatchJobsConfig, optional): List configuration including: + - page_size: Number of jobs per page + - page_token: Token for pagination + - filter: Filter expression + + Returns: + Union[Pager[BatchJob], Iterator[BatchJob]]: Paginated job list. + """ + ... + +class AsyncBatches: + """Asynchronous batch prediction jobs API.""" + + async def list( + self, + *, + config: Optional[ListBatchJobsConfig] = None + ) -> Union[AsyncPager[BatchJob], AsyncIterator[BatchJob]]: + """Async version of list.""" + ... +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, Iterator, AsyncIterator, Dict, Any +from datetime import datetime +from enum import Enum + +# Configuration types +class CreateBatchJobConfig: + """ + Configuration for creating batch jobs. + + Attributes: + display_name (str, optional): Display name for the job. + labels (dict[str, str], optional): Labels for the job. + """ + display_name: Optional[str] = None + labels: Optional[dict[str, str]] = None + +class ListBatchJobsConfig: + """ + Configuration for listing batch jobs. + + Attributes: + page_size (int, optional): Number of jobs per page. + page_token (str, optional): Token for pagination. + filter (str, optional): Filter expression (e.g., 'state=JOB_STATE_RUNNING'). + """ + page_size: Optional[int] = None + page_token: Optional[str] = None + filter: Optional[str] = None + +# Response types +class BatchJob: + """ + Batch prediction job information. + + Attributes: + name (str): Job resource name. + display_name (str, optional): Display name. + model (str): Model used for the job. + state (JobState): Current job state. + create_time (datetime): When job was created. + start_time (datetime, optional): When job started running. + end_time (datetime, optional): When job completed. + update_time (datetime): Last update time. + labels (dict[str, str], optional): Job labels. + metadata (BatchJobMetadata, optional): Job metadata and progress. + error (JobError, optional): Error if job failed. + """ + name: str + display_name: Optional[str] = None + model: str + state: JobState + create_time: datetime + start_time: Optional[datetime] = None + end_time: Optional[datetime] = None + update_time: datetime + labels: Optional[dict[str, str]] = None + metadata: Optional[BatchJobMetadata] = None + error: Optional[JobError] = None + +class BatchJobMetadata: + """ + Batch job metadata and progress. + + Attributes: + total_requests (int, optional): Total number of requests. + completed_requests (int, optional): Number of completed requests. + failed_requests (int, optional): Number of failed requests. + input_config (dict, optional): Input configuration. + output_config (dict, optional): Output configuration. + """ + total_requests: Optional[int] = None + completed_requests: Optional[int] = None + failed_requests: Optional[int] = None + input_config: Optional[dict] = None + output_config: Optional[dict] = None + +class JobState(Enum): + """Batch job states.""" + JOB_STATE_UNSPECIFIED = 'JOB_STATE_UNSPECIFIED' + JOB_STATE_QUEUED = 'JOB_STATE_QUEUED' + JOB_STATE_PENDING = 'JOB_STATE_PENDING' + JOB_STATE_RUNNING = 'JOB_STATE_RUNNING' + JOB_STATE_SUCCEEDED = 'JOB_STATE_SUCCEEDED' + JOB_STATE_FAILED = 'JOB_STATE_FAILED' + JOB_STATE_CANCELLING = 'JOB_STATE_CANCELLING' + JOB_STATE_CANCELLED = 'JOB_STATE_CANCELLED' + JOB_STATE_PAUSED = 'JOB_STATE_PAUSED' + JOB_STATE_EXPIRED = 'JOB_STATE_EXPIRED' + +class JobError: + """ + Job error information. + + Attributes: + code (int): Error code. + message (str): Error message. + details (list[dict], optional): Error details. + """ + code: int + message: str + details: Optional[list[dict]] = None + +# Pager types +class Pager[T]: + """Synchronous pager.""" + page: list[T] + def next_page(self) -> None: ... + def __iter__(self) -> Iterator[T]: ... + +class AsyncPager[T]: + """Asynchronous pager.""" + page: list[T] + async def next_page(self) -> None: ... + async def __aiter__(self) -> AsyncIterator[T]: ... +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/caching.md b/.tessl/tiles/tessl/pypi-google-genai/docs/caching.md new file mode 100644 index 000000000..12bab3f9f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/caching.md @@ -0,0 +1,455 @@ +# Context Caching + +Create and manage cached content to reduce costs and latency for repeated requests with shared context. Context caching stores frequently used content (like large documents or conversation context) so it doesn't need to be sent with every request, significantly reducing token costs and improving response times. + +## Capabilities + +### Create Cached Content + +Create a new cached content resource containing frequently used context. + +```python { .api } +class Caches: + """Synchronous cached content management API.""" + + def create( + self, + *, + model: str, + config: CreateCachedContentConfig + ) -> CachedContent: + """ + Create cached content for reuse across requests. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). + config (CreateCachedContentConfig): Cache configuration including: + - contents: Content to cache (documents, conversation history, etc.) + - system_instruction: System instruction to cache + - tools: Tool definitions to cache + - ttl: Time-to-live duration (e.g., '3600s' for 1 hour) + - expire_time: Absolute expiration time + - display_name: Display name for the cache + + Returns: + CachedContent: Created cache with name, expiration info, and metadata. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncCaches: + """Asynchronous cached content management API.""" + + async def create( + self, + *, + model: str, + config: CreateCachedContentConfig + ) -> CachedContent: + """Async version of create.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import ( + CreateCachedContentConfig, + Content, + Part +) + +client = Client(api_key='YOUR_API_KEY') + +# Create large document content +document = """ +[Large document text here - e.g., 50,000 tokens of product documentation] +""" + +config = CreateCachedContentConfig( + contents=[Content(parts=[Part(text=document)])], + system_instruction='You are a product support assistant.', + ttl='3600s', # Cache for 1 hour + display_name='Product Documentation Cache' +) + +cache = client.caches.create( + model='gemini-2.0-flash', + config=config +) + +print(f"Created cache: {cache.name}") +print(f"Expires: {cache.expire_time}") +print(f"Usage: {cache.usage_metadata.total_token_count} tokens") + +# Use cache in requests +from google.genai.types import GenerateContentConfig + +gen_config = GenerateContentConfig( + cached_content=cache.name +) + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='What is the return policy?', + config=gen_config +) +print(response.text) +``` + +### Get Cached Content + +Retrieve information about a cached content resource. + +```python { .api } +class Caches: + """Synchronous cached content management API.""" + + def get(self, *, name: str) -> CachedContent: + """ + Get cached content information. + + Parameters: + name (str): Cache name in format 'cachedContents/{cache_id}'. + + Returns: + CachedContent: Cache information including expiration and usage. + + Raises: + ClientError: For client errors including 404 if cache not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncCaches: + """Asynchronous cached content management API.""" + + async def get(self, *, name: str) -> CachedContent: + """Async version of get.""" + ... +``` + +### Update Cached Content + +Update cached content expiration time or other mutable fields. + +```python { .api } +class Caches: + """Synchronous cached content management API.""" + + def update( + self, + *, + name: str, + config: UpdateCachedContentConfig + ) -> CachedContent: + """ + Update cached content. + + Parameters: + name (str): Cache name in format 'cachedContents/{cache_id}'. + config (UpdateCachedContentConfig): Update configuration including: + - ttl: New time-to-live duration + - expire_time: New absolute expiration time + + Returns: + CachedContent: Updated cache information. + + Raises: + ClientError: For client errors including 404 if cache not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncCaches: + """Asynchronous cached content management API.""" + + async def update( + self, + *, + name: str, + config: UpdateCachedContentConfig + ) -> CachedContent: + """Async version of update.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import UpdateCachedContentConfig + +client = Client(api_key='YOUR_API_KEY') + +# Extend cache expiration +config = UpdateCachedContentConfig( + ttl='7200s' # Extend to 2 hours +) + +updated_cache = client.caches.update( + name='cachedContents/abc123', + config=config +) + +print(f"New expiration: {updated_cache.expire_time}") +``` + +### Delete Cached Content + +Delete a cached content resource. Caches are automatically deleted after expiration, but you can delete them early to free resources. + +```python { .api } +class Caches: + """Synchronous cached content management API.""" + + def delete(self, *, name: str) -> None: + """ + Delete cached content. + + Parameters: + name (str): Cache name in format 'cachedContents/{cache_id}'. + + Raises: + ClientError: For client errors including 404 if cache not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncCaches: + """Asynchronous cached content management API.""" + + async def delete(self, *, name: str) -> None: + """Async version of delete.""" + ... +``` + +### List Cached Contents + +List all cached content resources with optional pagination. + +```python { .api } +class Caches: + """Synchronous cached content management API.""" + + def list( + self, + *, + config: Optional[ListCachedContentsConfig] = None + ) -> Union[Pager[CachedContent], Iterator[CachedContent]]: + """ + List cached contents. + + Parameters: + config (ListCachedContentsConfig, optional): List configuration including: + - page_size: Number of items per page + - page_token: Token for pagination + + Returns: + Union[Pager[CachedContent], Iterator[CachedContent]]: Paginated cache list. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncCaches: + """Asynchronous cached content management API.""" + + async def list( + self, + *, + config: Optional[ListCachedContentsConfig] = None + ) -> Union[AsyncPager[CachedContent], AsyncIterator[CachedContent]]: + """Async version of list.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# List all caches +for cache in client.caches.list(): + print(f"{cache.display_name}: {cache.name}") + print(f" Expires: {cache.expire_time}") + print(f" Tokens: {cache.usage_metadata.total_token_count}") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, Iterator, AsyncIterator +from datetime import datetime +from enum import Enum + +# Configuration types +class CreateCachedContentConfig: + """ + Configuration for creating cached content. + + Attributes: + contents (list[Content]): Content to cache (documents, examples, etc.). + system_instruction (Union[str, Content], optional): System instruction to cache. + tools (list[Tool], optional): Tool definitions to cache. + tool_config (ToolConfig, optional): Tool configuration to cache. + ttl (str, optional): Time-to-live duration (e.g., '3600s', '1h'). Either ttl + or expire_time must be provided. + expire_time (datetime, optional): Absolute expiration time. Either ttl or + expire_time must be provided. + display_name (str, optional): Display name for the cache. + """ + contents: list[Content] + system_instruction: Optional[Union[str, Content]] = None + tools: Optional[list[Tool]] = None + tool_config: Optional[ToolConfig] = None + ttl: Optional[str] = None + expire_time: Optional[datetime] = None + display_name: Optional[str] = None + +class UpdateCachedContentConfig: + """ + Configuration for updating cached content. + + Attributes: + ttl (str, optional): New time-to-live duration. Either ttl or expire_time + must be provided. + expire_time (datetime, optional): New absolute expiration time. + """ + ttl: Optional[str] = None + expire_time: Optional[datetime] = None + +class ListCachedContentsConfig: + """ + Configuration for listing cached contents. + + Attributes: + page_size (int, optional): Number of items per page. + page_token (str, optional): Token for pagination. + """ + page_size: Optional[int] = None + page_token: Optional[str] = None + +# Response types +class CachedContent: + """ + Cached content resource. + + Attributes: + name (str): Resource name in format 'cachedContents/{cache_id}'. + model (str): Model identifier this cache is for. + display_name (str, optional): Display name. + contents (list[Content]): Cached content. + system_instruction (Content, optional): Cached system instruction. + tools (list[Tool], optional): Cached tools. + tool_config (ToolConfig, optional): Cached tool config. + create_time (datetime): When cache was created. + update_time (datetime): When cache was last updated. + expire_time (datetime): When cache will expire. + usage_metadata (CachedContentUsageMetadata): Token usage information. + """ + name: str + model: str + display_name: Optional[str] = None + contents: list[Content] + system_instruction: Optional[Content] = None + tools: Optional[list[Tool]] = None + tool_config: Optional[ToolConfig] = None + create_time: datetime + update_time: datetime + expire_time: datetime + usage_metadata: CachedContentUsageMetadata + +class CachedContentUsageMetadata: + """ + Token usage metadata for cached content. + + Attributes: + total_token_count (int): Total tokens in cached content. + """ + total_token_count: int + +# Core types (from content-generation) +class Content: + """Content container with role and parts.""" + parts: list[Part] + role: Optional[str] = None + +class Part: + """Individual content part.""" + text: Optional[str] = None + inline_data: Optional[Blob] = None + file_data: Optional[FileData] = None + +class Blob: + """Binary data with MIME type.""" + mime_type: str + data: bytes + +class FileData: + """Reference to uploaded file.""" + file_uri: str + mime_type: str + +class Tool: + """Tool with function declarations.""" + function_declarations: Optional[list[FunctionDeclaration]] = None + +class FunctionDeclaration: + """Function definition.""" + name: str + description: str + parameters: Optional[Schema] = None + +class ToolConfig: + """Tool configuration.""" + function_calling_config: Optional[FunctionCallingConfig] = None + +class FunctionCallingConfig: + """Function calling mode configuration.""" + mode: FunctionCallingConfigMode + allowed_function_names: Optional[list[str]] = None + +class FunctionCallingConfigMode(Enum): + """Function calling modes.""" + MODE_UNSPECIFIED = 'MODE_UNSPECIFIED' + AUTO = 'AUTO' + ANY = 'ANY' + NONE = 'NONE' + +class Schema: + """JSON schema.""" + type: Type + properties: Optional[dict[str, Schema]] = None + +class Type(Enum): + """JSON schema types.""" + TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED' + STRING = 'STRING' + NUMBER = 'NUMBER' + INTEGER = 'INTEGER' + BOOLEAN = 'BOOLEAN' + ARRAY = 'ARRAY' + OBJECT = 'OBJECT' + +# Pager types +class Pager[T]: + """Synchronous pager.""" + page: list[T] + def next_page(self) -> None: ... + def __iter__(self) -> Iterator[T]: ... + +class AsyncPager[T]: + """Asynchronous pager.""" + page: list[T] + async def next_page(self) -> None: ... + async def __aiter__(self) -> AsyncIterator[T]: ... +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/chats.md b/.tessl/tiles/tessl/pypi-google-genai/docs/chats.md new file mode 100644 index 000000000..b5b47cdca --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/chats.md @@ -0,0 +1,738 @@ +# Multi-Turn Conversations + +Create and manage chat sessions for multi-turn conversations with automatic history management. Chat sessions maintain conversation context and provide a convenient interface for back-and-forth interactions with the model. + +## Capabilities + +### Create Chat Session + +Create a new chat session with optional configuration and initial history. The chat session automatically manages conversation history across multiple turns. + +```python { .api } +class Chats: + """Factory for creating synchronous chat sessions.""" + + def create( + self, + *, + model: str, + config: Optional[GenerateContentConfig] = None, + history: Optional[list[Content]] = None + ) -> Chat: + """ + Create a new chat session. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). + config (GenerateContentConfig, optional): Default configuration for all messages + in this chat. Includes system instructions, generation config, safety settings, + tools, etc. Can be overridden per message. + history (list[Content], optional): Initial conversation history. Each Content + should have a role ('user' or 'model') and parts. + + Returns: + Chat: New chat session instance for sending messages. + """ + ... + +class AsyncChats: + """Factory for creating asynchronous chat sessions.""" + + def create( + self, + *, + model: str, + config: Optional[GenerateContentConfig] = None, + history: Optional[list[Content]] = None + ) -> AsyncChat: + """ + Create a new async chat session. + + Parameters: + model (str): Model identifier. + config (GenerateContentConfig, optional): Default configuration. + history (list[Content], optional): Initial conversation history. + + Returns: + AsyncChat: New async chat session instance. + """ + ... +``` + +**Usage Example - Create Chat:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Create chat session +chat = client.chats.create(model='gemini-2.0-flash') + +# Send messages +response1 = chat.send_message('What is machine learning?') +print(response1.text) + +response2 = chat.send_message('Can you give me an example?') +print(response2.text) + +# History is automatically maintained +history = chat.get_history() +print(f"Conversation has {len(history)} messages") +``` + +**Usage Example - With Configuration:** + +```python +from google.genai import Client +from google.genai.types import ( + GenerateContentConfig, + SafetySetting, + HarmCategory, + HarmBlockThreshold +) + +client = Client(api_key='YOUR_API_KEY') + +config = GenerateContentConfig( + system_instruction='You are a helpful coding assistant.', + temperature=0.3, + max_output_tokens=512, + safety_settings=[ + SafetySetting( + category=HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE + ) + ] +) + +chat = client.chats.create( + model='gemini-2.0-flash', + config=config +) + +response = chat.send_message('How do I sort a list in Python?') +print(response.text) +``` + +### Send Message + +Send a message to the chat session and receive a response. The message and response are automatically added to the conversation history. + +```python { .api } +class Chat: + """Synchronous chat session for multi-turn conversations.""" + + def send_message( + self, + message: Union[str, Part, Image, list[Part]], + config: Optional[GenerateContentConfig] = None + ) -> GenerateContentResponse: + """ + Send a message to the chat and receive a response. + + Parameters: + message (Union[str, Part, Image, list[Part]]): Message to send. Can be: + - str: Simple text message + - Part: Single part (text, inline_data, etc.) + - Image: Image to send with implicit question + - list[Part]: Multiple parts (e.g., text + image) + config (GenerateContentConfig, optional): Configuration override for this + message only. Merged with chat's default config. + + Returns: + GenerateContentResponse: Model response containing generated content, + usage metadata, and safety ratings. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncChat: + """Asynchronous chat session for multi-turn conversations.""" + + async def send_message( + self, + message: Union[str, Part, Image, list[Part]], + config: Optional[GenerateContentConfig] = None + ) -> GenerateContentResponse: + """ + Async version of send_message. + + Parameters: + message (Union[str, Part, Image, list[Part]]): Message to send. + config (GenerateContentConfig, optional): Configuration override. + + Returns: + GenerateContentResponse: Model response. + """ + ... +``` + +**Usage Example - Send Text Message:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') +chat = client.chats.create(model='gemini-2.0-flash') + +# Send text messages +response1 = chat.send_message('Tell me about Python.') +print(response1.text) + +response2 = chat.send_message('What are its main features?') +print(response2.text) +``` + +**Usage Example - Send Multimodal Message:** + +```python +from google.genai import Client +from google.genai.types import Part, Image + +client = Client(api_key='YOUR_API_KEY') +chat = client.chats.create(model='gemini-2.0-flash') + +# Send image with question +image = Image.from_file('diagram.png') +message_parts = [ + Part(text='What does this diagram show?'), + Part(inline_data=image.blob) +] + +response = chat.send_message(message_parts) +print(response.text) + +# Continue conversation +followup = chat.send_message('Can you explain it in more detail?') +print(followup.text) +``` + +### Send Message Streaming + +Send a message and receive the response as a stream of chunks, allowing for real-time display of the model's output. + +```python { .api } +class Chat: + """Synchronous chat session for multi-turn conversations.""" + + def send_message_stream( + self, + message: Union[str, Part, Image, list[Part]], + config: Optional[GenerateContentConfig] = None + ) -> Iterator[GenerateContentResponse]: + """ + Send a message to the chat and receive a streaming response. + + Parameters: + message (Union[str, Part, Image, list[Part]]): Message to send. + config (GenerateContentConfig, optional): Configuration override for this message. + + Yields: + GenerateContentResponse: Streaming response chunks as they are generated. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncChat: + """Asynchronous chat session for multi-turn conversations.""" + + async def send_message_stream( + self, + message: Union[str, Part, Image, list[Part]], + config: Optional[GenerateContentConfig] = None + ) -> AsyncIterator[GenerateContentResponse]: + """ + Async version of send_message_stream. + + Parameters: + message (Union[str, Part, Image, list[Part]]): Message to send. + config (GenerateContentConfig, optional): Configuration override. + + Yields: + GenerateContentResponse: Streaming response chunks. + """ + ... +``` + +**Usage Example - Streaming:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') +chat = client.chats.create(model='gemini-2.0-flash') + +# First message +response1 = chat.send_message('What is neural network?') +print(response1.text) + +# Streaming follow-up +print('\nModel response (streaming):') +stream = chat.send_message_stream('Explain backpropagation.') +for chunk in stream: + print(chunk.text, end='', flush=True) +print() # New line +``` + +**Usage Example - Async Streaming:** + +```python +import asyncio +from google.genai import Client + +async def main(): + client = Client(api_key='YOUR_API_KEY') + chat = client.aio.chats.create(model='gemini-2.0-flash') + + # First message + response = await chat.send_message('Hello!') + print(response.text) + + # Streaming follow-up + print('\nStreaming response:') + stream = await chat.send_message_stream('Tell me about AI.') + async for chunk in stream: + print(chunk.text, end='', flush=True) + print() + +asyncio.run(main()) +``` + +### Get History + +Retrieve the conversation history for the chat session. + +```python { .api } +class Chat: + """Synchronous chat session for multi-turn conversations.""" + + def get_history(self, curated: bool = False) -> list[Content]: + """ + Get the chat conversation history. + + Parameters: + curated (bool): If True, returns only user and model messages, filtering out + intermediate function calls and responses. If False, returns complete + history including all function calling interactions. Defaults to False. + + Returns: + list[Content]: List of Content objects representing the conversation history + in chronological order. Each Content has a role ('user' or 'model') and + list of parts. + """ + ... + +class AsyncChat: + """Asynchronous chat session for multi-turn conversations.""" + + def get_history(self, curated: bool = False) -> list[Content]: + """ + Get the chat conversation history. + + Note: This is a synchronous method even in AsyncChat as it doesn't require I/O. + + Parameters: + curated (bool): If True, returns curated history without function calls. + + Returns: + list[Content]: Conversation history. + """ + ... +``` + +**Usage Example - Get History:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') +chat = client.chats.create(model='gemini-2.0-flash') + +# Have a conversation +chat.send_message('What is Python?') +chat.send_message('What are its uses?') +chat.send_message('Is it beginner-friendly?') + +# Get full history +history = chat.get_history() +print(f"Total messages: {len(history)}") + +for i, content in enumerate(history): + role = content.role or 'unknown' + text = content.parts[0].text if content.parts else '' + print(f"{i+1}. {role}: {text[:50]}...") + +# Get curated history (without function call details) +curated_history = chat.get_history(curated=True) +print(f"Curated messages: {len(curated_history)}") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, Iterator, AsyncIterator, Any +from enum import Enum + +# Core types (shared with content-generation.md) +class Content: + """ + Container for conversation content with role and parts. + + Attributes: + parts (list[Part]): List of content parts + role (str, optional): Role ('user' or 'model') + """ + parts: list[Part] + role: Optional[str] = None + +class Part: + """ + Individual content part. + + Attributes: + text (str, optional): Text content + inline_data (Blob, optional): Inline binary data + file_data (FileData, optional): Reference to uploaded file + function_call (FunctionCall, optional): Function call from model + function_response (FunctionResponse, optional): Function execution result + executable_code (ExecutableCode, optional): Executable code + code_execution_result (CodeExecutionResult, optional): Code execution output + """ + text: Optional[str] = None + inline_data: Optional[Blob] = None + file_data: Optional[FileData] = None + function_call: Optional[FunctionCall] = None + function_response: Optional[FunctionResponse] = None + executable_code: Optional[ExecutableCode] = None + code_execution_result: Optional[CodeExecutionResult] = None + +class Blob: + """ + Binary data with MIME type. + + Attributes: + mime_type (str): MIME type + data (bytes): Binary data + """ + mime_type: str + data: bytes + +class FileData: + """ + Reference to uploaded file. + + Attributes: + file_uri (str): URI of uploaded file + mime_type (str): MIME type + """ + file_uri: str + mime_type: str + +class Image: + """Image data supporting multiple input formats.""" + pass + +class GenerateContentConfig: + """ + Configuration for content generation. + + Attributes: + system_instruction (Union[str, Content], optional): System instructions + contents (Union[str, list[Content], Content], optional): Override contents + generation_config (GenerationConfig, optional): Generation parameters + safety_settings (list[SafetySetting], optional): Safety filtering + tools (list[Tool], optional): Function declarations + tool_config (ToolConfig, optional): Function calling config + cached_content (str, optional): Cached content reference + """ + system_instruction: Optional[Union[str, Content]] = None + contents: Optional[Union[str, list[Content], Content]] = None + generation_config: Optional[GenerationConfig] = None + safety_settings: Optional[list[SafetySetting]] = None + tools: Optional[list[Tool]] = None + tool_config: Optional[ToolConfig] = None + cached_content: Optional[str] = None + +class GenerationConfig: + """ + Core generation parameters. + + Attributes: + temperature (float, optional): Sampling temperature (0.0-2.0) + top_p (float, optional): Nucleus sampling (0.0-1.0) + top_k (int, optional): Top-k sampling + max_output_tokens (int, optional): Maximum response tokens + stop_sequences (list[str], optional): Stop sequences + """ + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + max_output_tokens: Optional[int] = None + stop_sequences: Optional[list[str]] = None + +class GenerateContentResponse: + """ + Response from content generation. + + Attributes: + text (str): Text from first candidate + candidates (list[Candidate]): Generated candidates + usage_metadata (GenerateContentResponseUsageMetadata, optional): Usage stats + prompt_feedback (GenerateContentResponsePromptFeedback, optional): Prompt feedback + model_version (str, optional): Model version + """ + text: str + candidates: list[Candidate] + usage_metadata: Optional[GenerateContentResponseUsageMetadata] = None + prompt_feedback: Optional[GenerateContentResponsePromptFeedback] = None + model_version: Optional[str] = None + +class Candidate: + """ + Generated candidate. + + Attributes: + content (Content): Generated content + finish_reason (FinishReason, optional): Reason generation stopped + safety_ratings (list[SafetyRating], optional): Safety ratings + citation_metadata (CitationMetadata, optional): Citations + grounding_metadata (GroundingMetadata, optional): Grounding attribution + """ + content: Content + finish_reason: Optional[FinishReason] = None + safety_ratings: Optional[list[SafetyRating]] = None + citation_metadata: Optional[CitationMetadata] = None + grounding_metadata: Optional[GroundingMetadata] = None + +class GenerateContentResponseUsageMetadata: + """ + Token usage statistics. + + Attributes: + prompt_token_count (int): Prompt tokens + candidates_token_count (int): Generated tokens + total_token_count (int): Total tokens + cached_content_token_count (int, optional): Cached tokens + """ + prompt_token_count: int + candidates_token_count: int + total_token_count: int + cached_content_token_count: Optional[int] = None + +class GenerateContentResponsePromptFeedback: + """ + Prompt feedback. + + Attributes: + block_reason (BlockedReason, optional): Block reason + safety_ratings (list[SafetyRating], optional): Safety ratings + """ + block_reason: Optional[BlockedReason] = None + safety_ratings: Optional[list[SafetyRating]] = None + +class SafetySetting: + """ + Safety filter configuration. + + Attributes: + category (HarmCategory): Harm category + threshold (HarmBlockThreshold): Blocking threshold + """ + category: HarmCategory + threshold: HarmBlockThreshold + +class SafetyRating: + """ + Safety rating. + + Attributes: + category (HarmCategory): Harm category + probability (HarmProbability): Harm probability + blocked (bool): Whether blocked + """ + category: HarmCategory + probability: HarmProbability + blocked: bool + +class Tool: + """ + Tool with function declarations. + + Attributes: + function_declarations (list[FunctionDeclaration], optional): Functions + google_search (GoogleSearch, optional): Google Search tool + code_execution (ToolCodeExecution, optional): Code execution tool + """ + function_declarations: Optional[list[FunctionDeclaration]] = None + google_search: Optional[GoogleSearch] = None + code_execution: Optional[ToolCodeExecution] = None + +class FunctionDeclaration: + """ + Function definition. + + Attributes: + name (str): Function name + description (str): Function description + parameters (Schema, optional): Parameter schema + """ + name: str + description: str + parameters: Optional[Schema] = None + +class FunctionCall: + """ + Function call from model. + + Attributes: + name (str): Function name + args (dict[str, Any]): Arguments + id (str, optional): Call ID + """ + name: str + args: dict[str, Any] + id: Optional[str] = None + +class FunctionResponse: + """ + Function execution result. + + Attributes: + name (str): Function name + response (dict[str, Any]): Return value + id (str, optional): Call ID + """ + name: str + response: dict[str, Any] + id: Optional[str] = None + +class ToolConfig: + """Function calling configuration.""" + function_calling_config: Optional[FunctionCallingConfig] = None + +class FunctionCallingConfig: + """ + Function calling mode. + + Attributes: + mode (FunctionCallingConfigMode): Calling mode + allowed_function_names (list[str], optional): Allowed functions + """ + mode: FunctionCallingConfigMode + allowed_function_names: Optional[list[str]] = None + +class FunctionCallingConfigMode(Enum): + """Function calling modes.""" + MODE_UNSPECIFIED = 'MODE_UNSPECIFIED' + AUTO = 'AUTO' + ANY = 'ANY' + NONE = 'NONE' + +class Schema: + """JSON schema for parameters.""" + type: Type + properties: Optional[dict[str, Schema]] = None + required: Optional[list[str]] = None + +class Type(Enum): + """JSON schema types.""" + TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED' + STRING = 'STRING' + NUMBER = 'NUMBER' + INTEGER = 'INTEGER' + BOOLEAN = 'BOOLEAN' + ARRAY = 'ARRAY' + OBJECT = 'OBJECT' + +class FinishReason(Enum): + """Finish reasons.""" + FINISH_REASON_UNSPECIFIED = 'FINISH_REASON_UNSPECIFIED' + STOP = 'STOP' + MAX_TOKENS = 'MAX_TOKENS' + SAFETY = 'SAFETY' + RECITATION = 'RECITATION' + +class BlockedReason(Enum): + """Blocked reasons.""" + BLOCKED_REASON_UNSPECIFIED = 'BLOCKED_REASON_UNSPECIFIED' + SAFETY = 'SAFETY' + OTHER = 'OTHER' + +class HarmCategory(Enum): + """Harm categories.""" + HARM_CATEGORY_UNSPECIFIED = 'HARM_CATEGORY_UNSPECIFIED' + HARM_CATEGORY_HARASSMENT = 'HARM_CATEGORY_HARASSMENT' + HARM_CATEGORY_HATE_SPEECH = 'HARM_CATEGORY_HATE_SPEECH' + HARM_CATEGORY_SEXUALLY_EXPLICIT = 'HARM_CATEGORY_SEXUALLY_EXPLICIT' + HARM_CATEGORY_DANGEROUS_CONTENT = 'HARM_CATEGORY_DANGEROUS_CONTENT' + +class HarmBlockThreshold(Enum): + """Block thresholds.""" + HARM_BLOCK_THRESHOLD_UNSPECIFIED = 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' + BLOCK_LOW_AND_ABOVE = 'BLOCK_LOW_AND_ABOVE' + BLOCK_MEDIUM_AND_ABOVE = 'BLOCK_MEDIUM_AND_ABOVE' + BLOCK_ONLY_HIGH = 'BLOCK_ONLY_HIGH' + BLOCK_NONE = 'BLOCK_NONE' + +class HarmProbability(Enum): + """Harm probabilities.""" + HARM_PROBABILITY_UNSPECIFIED = 'HARM_PROBABILITY_UNSPECIFIED' + NEGLIGIBLE = 'NEGLIGIBLE' + LOW = 'LOW' + MEDIUM = 'MEDIUM' + HIGH = 'HIGH' + +class CitationMetadata: + """Citation information.""" + citations: list[Citation] + +class Citation: + """Individual citation.""" + start_index: int + end_index: int + uri: str + +class GroundingMetadata: + """Grounding attribution.""" + grounding_chunks: Optional[list[GroundingChunk]] = None + +class GroundingChunk: + """Grounding chunk.""" + pass + +class GoogleSearch: + """Google Search tool.""" + pass + +class ToolCodeExecution: + """Code execution tool.""" + pass + +class ExecutableCode: + """Executable code from model.""" + language: Language + code: str + +class Language(Enum): + """Programming languages.""" + LANGUAGE_UNSPECIFIED = 'LANGUAGE_UNSPECIFIED' + PYTHON = 'PYTHON' + +class CodeExecutionResult: + """Code execution result.""" + outcome: Outcome + output: str + +class Outcome(Enum): + """Execution outcomes.""" + OUTCOME_UNSPECIFIED = 'OUTCOME_UNSPECIFIED' + OUTCOME_OK = 'OUTCOME_OK' + OUTCOME_FAILED = 'OUTCOME_FAILED' +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/client.md b/.tessl/tiles/tessl/pypi-google-genai/docs/client.md new file mode 100644 index 000000000..a6d606510 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/client.md @@ -0,0 +1,463 @@ +# Client Initialization and Configuration + +Client initialization and configuration for accessing the Google GenAI Python SDK. The SDK supports both the Gemini Developer API (using API keys) and Vertex AI API (using Google Cloud credentials and project configuration). The Client class provides access to all SDK functionality through specialized API modules. + +## Capabilities + +### Client Initialization + +Initialize the main synchronous client to access all SDK functionality. The client automatically determines which API to use based on the provided configuration. + +```python { .api } +class Client: + """ + Primary synchronous client for making requests to Gemini Developer API or Vertex AI API. + + Parameters: + vertexai (bool, optional): Use Vertex AI API endpoints. Defaults to False (Gemini Developer API). + Can be set via GOOGLE_GENAI_USE_VERTEXAI environment variable. + api_key (str, optional): API key for Gemini Developer API authentication. + Can be set via GOOGLE_API_KEY environment variable. + credentials (google.auth.credentials.Credentials, optional): Google Cloud credentials for Vertex AI API. + Uses Application Default Credentials if not provided. + project (str, optional): Google Cloud project ID for Vertex AI API quota. + Can be set via GOOGLE_CLOUD_PROJECT environment variable. + location (str, optional): Google Cloud location for Vertex AI API (e.g., 'us-central1'). + Can be set via GOOGLE_CLOUD_LOCATION environment variable. + debug_config (DebugConfig, optional): Configuration for testing and debugging network behavior. + http_options (Union[HttpOptions, HttpOptionsDict], optional): HTTP client configuration options. + + Returns: + Client instance providing access to all SDK API modules. + """ + def __init__( + self, + *, + vertexai: Optional[bool] = None, + api_key: Optional[str] = None, + credentials: Optional[google.auth.credentials.Credentials] = None, + project: Optional[str] = None, + location: Optional[str] = None, + debug_config: Optional[DebugConfig] = None, + http_options: Optional[Union[HttpOptions, HttpOptionsDict]] = None + ): ... + + @property + def aio(self) -> AsyncClient: + """Access the async client interface for non-blocking operations.""" + ... + + @property + def models(self) -> Models: + """Access models API for content generation, embeddings, and image/video generation.""" + ... + + @property + def chats(self) -> Chats: + """Create multi-turn chat sessions with automatic history management.""" + ... + + @property + def files(self) -> Files: + """Access file management API (Gemini Developer API only).""" + ... + + @property + def caches(self) -> Caches: + """Access cached content API for context caching.""" + ... + + @property + def batches(self) -> Batches: + """Access batch prediction jobs API.""" + ... + + @property + def tunings(self) -> Tunings: + """Access tuning jobs API (Vertex AI only).""" + ... + + @property + def file_search_stores(self) -> FileSearchStores: + """Access file search stores API for retrieval-augmented generation.""" + ... + + @property + def auth_tokens(self) -> Tokens: + """Access authentication tokens API.""" + ... + + @property + def operations(self) -> Operations: + """Access long-running operations API.""" + ... + + @property + def vertexai(self) -> bool: + """Returns True if the client is using Vertex AI API, False otherwise.""" + ... + + def close(self) -> None: + """ + Close the synchronous client explicitly to release resources. + + Note: This does not close the async client. Use Client.aio.aclose() for async client. + """ + ... + + def __enter__(self) -> 'Client': + """Context manager entry for automatic resource cleanup.""" + ... + + def __exit__(self, *args) -> None: + """Context manager exit for automatic resource cleanup.""" + ... +``` + +**Usage Example - Gemini Developer API:** + +```python +from google.genai import Client + +# Initialize with API key +client = Client(api_key='YOUR_API_KEY') + +# Generate content +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Explain quantum computing' +) +print(response.text) + +# Close when done +client.close() +``` + +**Usage Example - Context Manager:** + +```python +from google.genai import Client + +with Client(api_key='YOUR_API_KEY') as client: + response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Hello!' + ) + print(response.text) +# Client automatically closes +``` + +**Usage Example - Vertex AI:** + +```python +from google.genai import Client + +# Initialize for Vertex AI +client = Client( + vertexai=True, + project='my-project-id', + location='us-central1' +) + +# Generate content +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Explain machine learning' +) +print(response.text) + +client.close() +``` + +### Async Client + +Access asynchronous (non-blocking) client for concurrent operations. The async client provides the same API modules as the sync client but with async/await support. + +```python { .api } +class AsyncClient: + """ + Asynchronous client for making non-blocking requests. + + Access via Client.aio property. All methods are async and require await. + """ + + @property + def models(self) -> AsyncModels: + """Access async models API.""" + ... + + @property + def chats(self) -> AsyncChats: + """Create async multi-turn chat sessions.""" + ... + + @property + def files(self) -> AsyncFiles: + """Access async file management API.""" + ... + + @property + def caches(self) -> AsyncCaches: + """Access async cached content API.""" + ... + + @property + def batches(self) -> AsyncBatches: + """Access async batch prediction jobs API.""" + ... + + @property + def tunings(self) -> AsyncTunings: + """Access async tuning jobs API.""" + ... + + @property + def file_search_stores(self) -> AsyncFileSearchStores: + """Access async file search stores API.""" + ... + + @property + def live(self) -> AsyncLive: + """Access async live API for real-time bidirectional streaming.""" + ... + + @property + def auth_tokens(self) -> AsyncTokens: + """Access async authentication tokens API.""" + ... + + @property + def operations(self) -> AsyncOperations: + """Access async long-running operations API.""" + ... + + async def aclose(self) -> None: + """ + Close the async client explicitly to release resources. + + Note: This does not close the sync client. Use Client.close() for sync client. + """ + ... + + async def __aenter__(self) -> 'AsyncClient': + """Async context manager entry.""" + ... + + async def __aexit__(self, *args) -> None: + """Async context manager exit.""" + ... +``` + +**Usage Example - Async Client:** + +```python +import asyncio +from google.genai import Client + +async def main(): + client = Client(api_key='YOUR_API_KEY') + async_client = client.aio + + # Make async request + response = await async_client.models.generate_content( + model='gemini-2.0-flash', + contents='What is AI?' + ) + print(response.text) + + # Close async client + await async_client.aclose() + client.close() + +asyncio.run(main()) +``` + +**Usage Example - Async Context Manager:** + +```python +import asyncio +from google.genai import Client + +async def main(): + client = Client(api_key='YOUR_API_KEY') + + async with client.aio as async_client: + response = await async_client.models.generate_content( + model='gemini-2.0-flash', + contents='Hello!' + ) + print(response.text) + + client.close() + +asyncio.run(main()) +``` + +### HTTP Configuration + +Configure HTTP client behavior including API version, timeouts, retries, and custom headers. + +```python { .api } +class HttpOptions: + """ + HTTP client configuration options. + + Parameters: + api_version (str, optional): API version to use (e.g., 'v1', 'v1beta'). Defaults to 'v1beta'. + base_url (str, optional): Override base URL for API requests. + timeout (float, optional): Request timeout in seconds. + headers (Dict[str, str], optional): Additional HTTP headers for all requests. + retry_options (HttpRetryOptions, optional): Retry configuration for failed requests. + """ + api_version: Optional[str] = None + base_url: Optional[str] = None + timeout: Optional[float] = None + headers: Optional[Dict[str, str]] = None + retry_options: Optional[HttpRetryOptions] = None + +class HttpRetryOptions: + """ + HTTP retry configuration for handling transient failures. + + Parameters: + max_retries (int, optional): Maximum number of retry attempts. Defaults to 3. + initial_backoff (float, optional): Initial backoff delay in seconds. Defaults to 1.0. + max_backoff (float, optional): Maximum backoff delay in seconds. Defaults to 60.0. + backoff_multiplier (float, optional): Backoff multiplier for exponential backoff. Defaults to 2.0. + retry_statuses (List[int], optional): HTTP status codes that trigger retries. + Defaults to [408, 429, 500, 502, 503, 504]. + """ + max_retries: Optional[int] = None + initial_backoff: Optional[float] = None + max_backoff: Optional[float] = None + backoff_multiplier: Optional[float] = None + retry_statuses: Optional[List[int]] = None + +# TypedDict variants for flexible usage +class HttpOptionsDict(TypedDict, total=False): + api_version: str + base_url: str + timeout: float + headers: Dict[str, str] + retry_options: Union[HttpRetryOptions, 'HttpRetryOptionsDict'] + +class HttpRetryOptionsDict(TypedDict, total=False): + max_retries: int + initial_backoff: float + max_backoff: float + backoff_multiplier: float + retry_statuses: List[int] +``` + +**Usage Example - HTTP Configuration:** + +```python +from google.genai import Client +from google.genai.types import HttpOptions, HttpRetryOptions + +# Configure HTTP options +http_options = HttpOptions( + api_version='v1', + timeout=30.0, + headers={'User-Agent': 'MyApp/1.0'}, + retry_options=HttpRetryOptions( + max_retries=5, + initial_backoff=2.0, + max_backoff=120.0 + ) +) + +client = Client( + api_key='YOUR_API_KEY', + http_options=http_options +) + +# All requests use these HTTP options +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Hello' +) +``` + +### Debug Configuration + +Configure client behavior for testing and debugging, including record/replay functionality for deterministic testing. + +```python { .api } +class DebugConfig: + """ + Configuration options for testing and debugging client network behavior. + + Parameters: + client_mode (str, optional): Client operation mode. Options: + - 'record': Record API requests and responses to replay files + - 'replay': Replay API responses from recorded files + - 'auto': Automatically record if replay file doesn't exist, otherwise replay + Can be set via GOOGLE_GENAI_CLIENT_MODE environment variable. + replays_directory (str, optional): Directory for storing replay files. + Can be set via GOOGLE_GENAI_REPLAYS_DIRECTORY environment variable. + replay_id (str, optional): Identifier for replay file. + Can be set via GOOGLE_GENAI_REPLAY_ID environment variable. + """ + client_mode: Optional[str] = None + replays_directory: Optional[str] = None + replay_id: Optional[str] = None +``` + +**Usage Example - Debug Configuration:** + +```python +from google.genai import Client +from google.genai.client import DebugConfig + +# Configure for recording API interactions +debug_config = DebugConfig( + client_mode='record', + replays_directory='./test_replays', + replay_id='test_001' +) + +client = Client( + api_key='YOUR_API_KEY', + debug_config=debug_config +) + +# Requests are recorded to ./test_replays/test_001.json +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Test prompt' +) + +# Later, replay the recorded interactions +debug_config = DebugConfig( + client_mode='replay', + replays_directory='./test_replays', + replay_id='test_001' +) + +# Responses are replayed from recorded file +``` + +## Types + +```python { .api } +from typing import Optional, Union, Dict, List, TypedDict +import google.auth.credentials + +# Credentials type from google.auth +Credentials = google.auth.credentials.Credentials + +# HTTP response metadata +class HttpResponse: + """ + HTTP response metadata from API requests. + + Attributes: + status_code (int): HTTP status code + headers (Dict[str, str]): Response headers + request_url (str): URL of the request + """ + status_code: int + headers: Dict[str, str] + request_url: str +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md b/.tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md new file mode 100644 index 000000000..1895eed5e --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md @@ -0,0 +1,986 @@ +# Content Generation + +Generate text and multimodal content using Gemini models with support for streaming, function calling, structured output, safety controls, and extensive configuration options. Content generation is the primary capability for creating AI-generated responses from text, image, audio, video, and document inputs. + +## Capabilities + +### Generate Content + +Generate content synchronously from text and multimodal inputs. Supports single or multiple content inputs, system instructions, function calling, structured output, and safety settings. + +```python { .api } +def generate_content( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[GenerateContentConfig] = None +) -> GenerateContentResponse: + """ + Generate content from the model. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). + contents (Union[str, list[Content], Content]): Input content. Can be: + - str: Simple text prompt + - Content: Single content object with role and parts + - list[Content]: Multiple content objects for conversation history + config (GenerateContentConfig, optional): Generation configuration including: + - system_instruction: System-level instructions for the model + - generation_config: Parameters like temperature, top_p, max_tokens + - safety_settings: Content safety filtering configuration + - tools: Function declarations for function calling + - tool_config: Function calling behavior configuration + - cached_content: Reference to cached content for efficiency + + Returns: + GenerateContentResponse: Response containing generated content, usage metadata, + and safety ratings. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def generate_content( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[GenerateContentConfig] = None +) -> GenerateContentResponse: + """Async version of generate_content.""" + ... +``` + +**Usage Example - Simple Text Generation:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Explain quantum computing in simple terms.' +) + +print(response.text) +``` + +**Usage Example - With Configuration:** + +```python +from google.genai import Client +from google.genai.types import GenerateContentConfig + +client = Client(api_key='YOUR_API_KEY') + +config = GenerateContentConfig( + system_instruction='You are a helpful physics tutor.', + temperature=0.7, + top_p=0.95, + max_output_tokens=1024, + stop_sequences=['END'] +) + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Explain relativity', + config=config +) + +print(response.text) +``` + +**Usage Example - Multimodal Input:** + +```python +from google.genai import Client +from google.genai.types import Content, Part, Image + +client = Client(api_key='YOUR_API_KEY') + +# Create multimodal content +image = Image.from_file('photo.jpg') +content = Content( + parts=[ + Part(text='What is in this image?'), + Part(inline_data=image.blob) + ] +) + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents=content +) + +print(response.text) +``` + +### Generate Content Streaming + +Generate content with streaming responses, allowing you to receive and process chunks as they are generated rather than waiting for the complete response. + +```python { .api } +def generate_content_stream( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[GenerateContentConfig] = None +) -> Iterator[GenerateContentResponse]: + """ + Generate content in streaming mode, yielding response chunks as they are generated. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). + contents (Union[str, list[Content], Content]): Input content. + config (GenerateContentConfig, optional): Generation configuration. + + Yields: + GenerateContentResponse: Streaming response chunks. Each chunk contains: + - Partial or complete candidates with generated content + - Incremental usage metadata + - Safety ratings + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def generate_content_stream( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[GenerateContentConfig] = None +) -> AsyncIterator[GenerateContentResponse]: + """Async version of generate_content_stream.""" + ... +``` + +**Usage Example - Streaming:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +stream = client.models.generate_content_stream( + model='gemini-2.0-flash', + contents='Write a short story about a robot.' +) + +for chunk in stream: + print(chunk.text, end='', flush=True) +print() # New line after streaming completes +``` + +**Usage Example - Async Streaming:** + +```python +import asyncio +from google.genai import Client + +async def main(): + client = Client(api_key='YOUR_API_KEY') + + stream = await client.aio.models.generate_content_stream( + model='gemini-2.0-flash', + contents='Explain neural networks.' + ) + + async for chunk in stream: + print(chunk.text, end='', flush=True) + print() + +asyncio.run(main()) +``` + +### Function Calling + +Enable the model to call functions you define, allowing it to access external tools, APIs, and data sources. The model generates function calls based on the conversation context, and you execute them and return results. + +**Usage Example - Function Calling:** + +```python +from google.genai import Client +from google.genai.types import ( + GenerateContentConfig, + Tool, + FunctionDeclaration, + Schema, + Type, + FunctionResponse, + Content, + Part +) + +client = Client(api_key='YOUR_API_KEY') + +# Define function declarations +get_weather = FunctionDeclaration( + name='get_weather', + description='Get the current weather for a location', + parameters=Schema( + type=Type.OBJECT, + properties={ + 'location': Schema(type=Type.STRING, description='City name'), + 'unit': Schema( + type=Type.STRING, + enum=['celsius', 'fahrenheit'], + description='Temperature unit' + ) + }, + required=['location'] + ) +) + +config = GenerateContentConfig( + tools=[Tool(function_declarations=[get_weather])] +) + +# Initial request +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='What is the weather in Tokyo?', + config=config +) + +# Check if model wants to call function +if response.candidates[0].content.parts[0].function_call: + function_call = response.candidates[0].content.parts[0].function_call + + # Execute function (example) + if function_call.name == 'get_weather': + weather_data = {'temperature': 22, 'condition': 'sunny'} + + # Send function response back + function_response = FunctionResponse( + name='get_weather', + response=weather_data + ) + + # Continue conversation with function result + response2 = client.models.generate_content( + model='gemini-2.0-flash', + contents=[ + response.candidates[0].content, + Content(parts=[Part(function_response=function_response)]) + ], + config=config + ) + + print(response2.text) +``` + +### Structured Output + +Generate structured output conforming to a JSON schema, ensuring the model's response follows a specific format. + +**Usage Example - Structured Output:** + +```python +from google.genai import Client +from google.genai.types import ( + GenerateContentConfig, + Schema, + Type +) + +client = Client(api_key='YOUR_API_KEY') + +# Define output schema +recipe_schema = Schema( + type=Type.OBJECT, + properties={ + 'recipe_name': Schema(type=Type.STRING), + 'ingredients': Schema( + type=Type.ARRAY, + items=Schema(type=Type.STRING) + ), + 'steps': Schema( + type=Type.ARRAY, + items=Schema(type=Type.STRING) + ), + 'prep_time_minutes': Schema(type=Type.INTEGER) + }, + required=['recipe_name', 'ingredients', 'steps'] +) + +config = GenerateContentConfig( + response_mime_type='application/json', + response_schema=recipe_schema +) + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Give me a recipe for chocolate chip cookies', + config=config +) + +import json +recipe = json.loads(response.text) +print(recipe['recipe_name']) +print(f"Prep time: {recipe['prep_time_minutes']} minutes") +``` + +### Safety Settings + +Configure content safety filtering to control what types of content are blocked or allowed in both inputs and outputs. + +**Usage Example - Safety Settings:** + +```python +from google.genai import Client +from google.genai.types import ( + GenerateContentConfig, + SafetySetting, + HarmCategory, + HarmBlockThreshold +) + +client = Client(api_key='YOUR_API_KEY') + +config = GenerateContentConfig( + safety_settings=[ + SafetySetting( + category=HarmCategory.HARM_CATEGORY_HARASSMENT, + threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE + ), + SafetySetting( + category=HarmCategory.HARM_CATEGORY_HATE_SPEECH, + threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE + ), + SafetySetting( + category=HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, + threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE + ), + SafetySetting( + category=HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, + threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE + ) + ] +) + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Your prompt here', + config=config +) + +# Check safety ratings +for rating in response.candidates[0].safety_ratings: + print(f"{rating.category}: {rating.probability}") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, Sequence, Any, Dict, Iterator, AsyncIterator +from enum import Enum + +# Core content types +class Content: + """ + Container for conversation content with role and parts. + + Attributes: + parts (list[Part]): List of content parts (text, images, function calls, etc.) + role (str, optional): Role of the content creator ('user' or 'model') + """ + parts: list[Part] + role: Optional[str] = None + +class Part: + """ + Individual content part within a Content object. + + Attributes: + text (str, optional): Text content + inline_data (Blob, optional): Inline binary data (images, audio, etc.) + file_data (FileData, optional): Reference to uploaded file + function_call (FunctionCall, optional): Function call from model + function_response (FunctionResponse, optional): Function execution result + executable_code (ExecutableCode, optional): Executable code from model + code_execution_result (CodeExecutionResult, optional): Code execution output + """ + text: Optional[str] = None + inline_data: Optional[Blob] = None + file_data: Optional[FileData] = None + function_call: Optional[FunctionCall] = None + function_response: Optional[FunctionResponse] = None + executable_code: Optional[ExecutableCode] = None + code_execution_result: Optional[CodeExecutionResult] = None + +class Blob: + """ + Binary data with MIME type. + + Attributes: + mime_type (str): MIME type (e.g., 'image/jpeg', 'audio/wav') + data (bytes): Binary data + """ + mime_type: str + data: bytes + +class FileData: + """ + Reference to an uploaded file. + + Attributes: + file_uri (str): URI of the uploaded file (e.g., 'gs://bucket/file') + mime_type (str): MIME type of the file + """ + file_uri: str + mime_type: str + +class Image: + """ + Image data supporting multiple input formats. + + Can be created from: + - URL: Image.from_url('https://...') + - File path: Image.from_file('path/to/image.jpg') + - Bytes: Image.from_bytes(data, mime_type='image/jpeg') + - PIL Image: Image.from_pil(pil_image) + - FileData: Image(file_data=FileData(...)) + """ + pass + +class Video: + """ + Video data supporting multiple input formats. + + Can be created from: + - URL: Video.from_url('https://...') + - File path: Video.from_file('path/to/video.mp4') + - Bytes: Video.from_bytes(data, mime_type='video/mp4') + - FileData: Video(file_data=FileData(...)) + """ + pass + +# Generation configuration +class GenerateContentConfig: + """ + Configuration for content generation. + + Attributes: + system_instruction (Union[str, Content], optional): System-level instructions + contents (Union[str, list[Content], Content], optional): Override input contents + temperature (float, optional): Sampling temperature (0.0-2.0). Higher = more random. + top_p (float, optional): Nucleus sampling threshold (0.0-1.0) + top_k (float, optional): Top-k sampling parameter + candidate_count (int, optional): Number of response candidates to generate + max_output_tokens (int, optional): Maximum tokens in generated response + stop_sequences (list[str], optional): Sequences that stop generation + response_mime_type (str, optional): MIME type for structured output ('application/json') + response_schema (Schema, optional): JSON schema for structured output + presence_penalty (float, optional): Penalty for token presence (-2.0 to 2.0) + frequency_penalty (float, optional): Penalty for token frequency (-2.0 to 2.0) + response_logprobs (bool, optional): Include log probabilities in response + logprobs (int, optional): Number of top logprobs to return per token + safety_settings (list[SafetySetting], optional): Safety filtering configuration + tools (list[Tool], optional): Function declarations for function calling + tool_config (ToolConfig, optional): Function calling behavior configuration + cached_content (str, optional): Reference to cached content by name + """ + system_instruction: Optional[Union[str, Content]] = None + contents: Optional[Union[str, list[Content], Content]] = None + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[float] = None + candidate_count: Optional[int] = None + max_output_tokens: Optional[int] = None + stop_sequences: Optional[list[str]] = None + response_mime_type: Optional[str] = None + response_schema: Optional[Schema] = None + presence_penalty: Optional[float] = None + frequency_penalty: Optional[float] = None + response_logprobs: Optional[bool] = None + logprobs: Optional[int] = None + safety_settings: Optional[list[SafetySetting]] = None + tools: Optional[list[Tool]] = None + tool_config: Optional[ToolConfig] = None + cached_content: Optional[str] = None + +class GenerationConfig: + """ + NOTE: This type is not used directly. Generation parameters are passed directly + to GenerateContentConfig, not as a nested GenerationConfig object. + + Core generation parameters controlling model behavior. + + Attributes: + temperature (float, optional): Sampling temperature (0.0-2.0). Higher = more random. + top_p (float, optional): Nucleus sampling threshold (0.0-1.0) + top_k (int, optional): Top-k sampling parameter + candidate_count (int, optional): Number of response candidates to generate + max_output_tokens (int, optional): Maximum tokens in generated response + stop_sequences (list[str], optional): Sequences that stop generation + response_mime_type (str, optional): MIME type for structured output ('application/json') + response_schema (Schema, optional): JSON schema for structured output + presence_penalty (float, optional): Penalty for token presence (-2.0 to 2.0) + frequency_penalty (float, optional): Penalty for token frequency (-2.0 to 2.0) + response_logprobs (bool, optional): Include log probabilities in response + logprobs (int, optional): Number of top logprobs to return per token + """ + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + candidate_count: Optional[int] = None + max_output_tokens: Optional[int] = None + stop_sequences: Optional[list[str]] = None + response_mime_type: Optional[str] = None + response_schema: Optional[Schema] = None + presence_penalty: Optional[float] = None + frequency_penalty: Optional[float] = None + response_logprobs: Optional[bool] = None + logprobs: Optional[int] = None + +# Response types +class GenerateContentResponse: + """ + Response from content generation. + + Attributes: + text (str): Convenience property returning text from first candidate + candidates (list[Candidate]): Generated candidates with content and metadata + usage_metadata (GenerateContentResponseUsageMetadata, optional): Token usage stats + prompt_feedback (GenerateContentResponsePromptFeedback, optional): Prompt feedback + model_version (str, optional): Model version used for generation + """ + text: str + candidates: list[Candidate] + usage_metadata: Optional[GenerateContentResponseUsageMetadata] = None + prompt_feedback: Optional[GenerateContentResponsePromptFeedback] = None + model_version: Optional[str] = None + +class Candidate: + """ + Generated candidate with content and metadata. + + Attributes: + content (Content): Generated content + finish_reason (FinishReason, optional): Reason generation stopped + safety_ratings (list[SafetyRating], optional): Safety ratings for content + citation_metadata (CitationMetadata, optional): Citation information + grounding_metadata (GroundingMetadata, optional): Grounding attribution + token_count (int, optional): Number of tokens in candidate + index (int, optional): Candidate index + logprobs_result (LogprobsResult, optional): Log probabilities + """ + content: Content + finish_reason: Optional[FinishReason] = None + safety_ratings: Optional[list[SafetyRating]] = None + citation_metadata: Optional[CitationMetadata] = None + grounding_metadata: Optional[GroundingMetadata] = None + token_count: Optional[int] = None + index: Optional[int] = None + logprobs_result: Optional[LogprobsResult] = None + +class GenerateContentResponseUsageMetadata: + """ + Token usage statistics. + + Attributes: + prompt_token_count (int): Tokens in the prompt + candidates_token_count (int): Tokens in generated candidates + total_token_count (int): Total tokens (prompt + candidates) + cached_content_token_count (int, optional): Tokens from cached content + """ + prompt_token_count: int + candidates_token_count: int + total_token_count: int + cached_content_token_count: Optional[int] = None + +class GenerateContentResponsePromptFeedback: + """ + Feedback about the prompt. + + Attributes: + block_reason (BlockedReason, optional): Reason prompt was blocked + safety_ratings (list[SafetyRating], optional): Safety ratings for prompt + """ + block_reason: Optional[BlockedReason] = None + safety_ratings: Optional[list[SafetyRating]] = None + +# Safety types +class SafetySetting: + """ + Safety filter configuration. + + Attributes: + category (HarmCategory): Harm category to configure + threshold (HarmBlockThreshold): Blocking threshold for this category + method (HarmBlockMethod, optional): Block based on probability or severity + """ + category: HarmCategory + threshold: HarmBlockThreshold + method: Optional[HarmBlockMethod] = None + +class SafetyRating: + """ + Safety rating for content. + + Attributes: + category (HarmCategory): Harm category + probability (HarmProbability): Probability of harm + severity (HarmSeverity, optional): Severity of harm + blocked (bool): Whether content was blocked + """ + category: HarmCategory + probability: HarmProbability + severity: Optional[HarmSeverity] = None + blocked: bool + +class HarmCategory(Enum): + """Harm categories for safety filtering.""" + HARM_CATEGORY_UNSPECIFIED = 'HARM_CATEGORY_UNSPECIFIED' + HARM_CATEGORY_HARASSMENT = 'HARM_CATEGORY_HARASSMENT' + HARM_CATEGORY_HATE_SPEECH = 'HARM_CATEGORY_HATE_SPEECH' + HARM_CATEGORY_SEXUALLY_EXPLICIT = 'HARM_CATEGORY_SEXUALLY_EXPLICIT' + HARM_CATEGORY_DANGEROUS_CONTENT = 'HARM_CATEGORY_DANGEROUS_CONTENT' + HARM_CATEGORY_CIVIC_INTEGRITY = 'HARM_CATEGORY_CIVIC_INTEGRITY' + +class HarmBlockThreshold(Enum): + """Blocking thresholds for safety filtering.""" + HARM_BLOCK_THRESHOLD_UNSPECIFIED = 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' + BLOCK_LOW_AND_ABOVE = 'BLOCK_LOW_AND_ABOVE' + BLOCK_MEDIUM_AND_ABOVE = 'BLOCK_MEDIUM_AND_ABOVE' + BLOCK_ONLY_HIGH = 'BLOCK_ONLY_HIGH' + BLOCK_NONE = 'BLOCK_NONE' + OFF = 'OFF' + +class HarmProbability(Enum): + """Harm probability levels.""" + HARM_PROBABILITY_UNSPECIFIED = 'HARM_PROBABILITY_UNSPECIFIED' + NEGLIGIBLE = 'NEGLIGIBLE' + LOW = 'LOW' + MEDIUM = 'MEDIUM' + HIGH = 'HIGH' + +class HarmSeverity(Enum): + """Harm severity levels.""" + HARM_SEVERITY_UNSPECIFIED = 'HARM_SEVERITY_UNSPECIFIED' + HARM_SEVERITY_NEGLIGIBLE = 'HARM_SEVERITY_NEGLIGIBLE' + HARM_SEVERITY_LOW = 'HARM_SEVERITY_LOW' + HARM_SEVERITY_MEDIUM = 'HARM_SEVERITY_MEDIUM' + HARM_SEVERITY_HIGH = 'HARM_SEVERITY_HIGH' + +class HarmBlockMethod(Enum): + """Block method for safety filtering.""" + HARM_BLOCK_METHOD_UNSPECIFIED = 'HARM_BLOCK_METHOD_UNSPECIFIED' + SEVERITY = 'SEVERITY' + PROBABILITY = 'PROBABILITY' + +class FinishReason(Enum): + """Reasons why model stopped generating.""" + FINISH_REASON_UNSPECIFIED = 'FINISH_REASON_UNSPECIFIED' + STOP = 'STOP' + MAX_TOKENS = 'MAX_TOKENS' + SAFETY = 'SAFETY' + RECITATION = 'RECITATION' + LANGUAGE = 'LANGUAGE' + OTHER = 'OTHER' + BLOCKLIST = 'BLOCKLIST' + PROHIBITED_CONTENT = 'PROHIBITED_CONTENT' + SPII = 'SPII' + MALFORMED_FUNCTION_CALL = 'MALFORMED_FUNCTION_CALL' + +class BlockedReason(Enum): + """Reasons why prompt was blocked.""" + BLOCKED_REASON_UNSPECIFIED = 'BLOCKED_REASON_UNSPECIFIED' + SAFETY = 'SAFETY' + OTHER = 'OTHER' + BLOCKLIST = 'BLOCKLIST' + PROHIBITED_CONTENT = 'PROHIBITED_CONTENT' + +# Function calling types +class Tool: + """ + Tool containing function declarations. + + Attributes: + function_declarations (list[FunctionDeclaration], optional): Function definitions + google_search (GoogleSearch, optional): Google Search tool + code_execution (ToolCodeExecution, optional): Code execution tool + """ + function_declarations: Optional[list[FunctionDeclaration]] = None + google_search: Optional[GoogleSearch] = None + code_execution: Optional[ToolCodeExecution] = None + +class FunctionDeclaration: + """ + Function schema definition for function calling. + + Attributes: + name (str): Function name + description (str): Function description for the model + parameters (Schema, optional): JSON schema for function parameters + """ + name: str + description: str + parameters: Optional[Schema] = None + +class FunctionCall: + """ + Function invocation from model. + + Attributes: + name (str): Function name to call + args (dict[str, Any]): Function arguments + id (str, optional): Unique call identifier + """ + name: str + args: dict[str, Any] + id: Optional[str] = None + +class FunctionResponse: + """ + Function execution response to send back to model. + + Attributes: + name (str): Function name that was called + response (dict[str, Any]): Function return value + id (str, optional): Call identifier matching FunctionCall.id + """ + name: str + response: dict[str, Any] + id: Optional[str] = None + +class ToolConfig: + """ + Configuration for function calling behavior. + + Attributes: + function_calling_config (FunctionCallingConfig, optional): Function calling mode + """ + function_calling_config: Optional[FunctionCallingConfig] = None + +class FunctionCallingConfig: + """ + Function calling mode configuration. + + Attributes: + mode (FunctionCallingConfigMode): Calling mode (AUTO, ANY, NONE) + allowed_function_names (list[str], optional): Restrict to specific functions + """ + mode: FunctionCallingConfigMode + allowed_function_names: Optional[list[str]] = None + +class FunctionCallingConfigMode(Enum): + """Function calling modes.""" + MODE_UNSPECIFIED = 'MODE_UNSPECIFIED' + AUTO = 'AUTO' + ANY = 'ANY' + NONE = 'NONE' + +class Schema: + """ + JSON schema for structured output and function parameters. + + Attributes: + type (Type): Schema type (OBJECT, ARRAY, STRING, NUMBER, etc.) + format (str, optional): Format specifier + description (str, optional): Schema description + nullable (bool, optional): Whether value can be null + enum (list[str], optional): Allowed values for enums + properties (dict[str, Schema], optional): Object properties + required (list[str], optional): Required property names + items (Schema, optional): Schema for array items + """ + type: Type + format: Optional[str] = None + description: Optional[str] = None + nullable: Optional[bool] = None + enum: Optional[list[str]] = None + properties: Optional[dict[str, Schema]] = None + required: Optional[list[str]] = None + items: Optional[Schema] = None + +class Type(Enum): + """JSON schema types.""" + TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED' + STRING = 'STRING' + NUMBER = 'NUMBER' + INTEGER = 'INTEGER' + BOOLEAN = 'BOOLEAN' + ARRAY = 'ARRAY' + OBJECT = 'OBJECT' + NULL = 'NULL' + +class GoogleSearch: + """Google Search tool for web grounding.""" + pass + +class ToolCodeExecution: + """Code execution tool for running generated code.""" + pass + +class ExecutableCode: + """ + Executable code from model. + + Attributes: + language (Language): Programming language + code (str): Code to execute + """ + language: Language + code: str + +class CodeExecutionResult: + """ + Code execution result. + + Attributes: + outcome (Outcome): Execution outcome (OK, FAILED, DEADLINE_EXCEEDED) + output (str): Execution output + """ + outcome: Outcome + output: str + +class Language(Enum): + """Programming languages for code execution.""" + LANGUAGE_UNSPECIFIED = 'LANGUAGE_UNSPECIFIED' + PYTHON = 'PYTHON' + +class Outcome(Enum): + """Code execution outcomes.""" + OUTCOME_UNSPECIFIED = 'OUTCOME_UNSPECIFIED' + OUTCOME_OK = 'OUTCOME_OK' + OUTCOME_FAILED = 'OUTCOME_FAILED' + OUTCOME_DEADLINE_EXCEEDED = 'OUTCOME_DEADLINE_EXCEEDED' + +# Additional metadata types +class CitationMetadata: + """ + Citation information for generated content. + + Attributes: + citations (list[Citation]): List of citations + """ + citations: list[Citation] + +class Citation: + """ + Individual citation. + + Attributes: + start_index (int): Start character index in generated text + end_index (int): End character index in generated text + uri (str): Citation source URI + title (str, optional): Citation source title + license (str, optional): Content license + publication_date (GoogleTypeDate, optional): Publication date + """ + start_index: int + end_index: int + uri: str + title: Optional[str] = None + license: Optional[str] = None + publication_date: Optional[GoogleTypeDate] = None + +class GroundingMetadata: + """ + Grounding attribution metadata. + + Attributes: + grounding_chunks (list[GroundingChunk], optional): Grounding sources + grounding_supports (list[GroundingSupport], optional): Support scores + web_search_queries (list[str], optional): Web search queries used + search_entry_point (SearchEntryPoint, optional): Search entry point + """ + grounding_chunks: Optional[list[GroundingChunk]] = None + grounding_supports: Optional[list[GroundingSupport]] = None + web_search_queries: Optional[list[str]] = None + search_entry_point: Optional[SearchEntryPoint] = None + +class GroundingChunk: + """Grounding source chunk (web, retrieved context, etc.).""" + pass + +class GroundingSupport: + """ + Grounding support score. + + Attributes: + segment (Segment): Text segment + grounding_chunk_indices (list[int]): Indices of supporting chunks + confidence_scores (list[float]): Confidence scores + """ + segment: Segment + grounding_chunk_indices: list[int] + confidence_scores: list[float] + +class SearchEntryPoint: + """ + Search entry point for web grounding. + + Attributes: + rendered_content (str): Rendered search content + sdk_blob (bytes, optional): SDK blob data + """ + rendered_content: str + sdk_blob: Optional[bytes] = None + +class Segment: + """ + Content segment. + + Attributes: + part_index (int): Part index in content + start_index (int): Start character index + end_index (int): End character index + text (str, optional): Segment text + """ + part_index: int + start_index: int + end_index: int + text: Optional[str] = None + +class LogprobsResult: + """ + Log probabilities result. + + Attributes: + top_candidates (list[LogprobsResultTopCandidates]): Top candidates per token + chosen_candidates (list[LogprobsResultCandidate]): Chosen candidates + """ + top_candidates: list[LogprobsResultTopCandidates] + chosen_candidates: list[LogprobsResultCandidate] + +class LogprobsResultTopCandidates: + """ + Top candidate tokens and probabilities. + + Attributes: + candidates (list[LogprobsResultCandidate]): Candidate tokens + """ + candidates: list[LogprobsResultCandidate] + +class LogprobsResultCandidate: + """ + Individual token candidate. + + Attributes: + token (str): Token string + token_id (int): Token ID + log_probability (float): Log probability + """ + token: str + token_id: int + log_probability: float + +class GoogleTypeDate: + """ + Date representation. + + Attributes: + year (int): Year + month (int): Month (1-12) + day (int): Day (1-31) + """ + year: int + month: int + day: int +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md b/.tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md new file mode 100644 index 000000000..58b34bad8 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md @@ -0,0 +1,346 @@ +# Embeddings + +Generate text embeddings for semantic search, clustering, similarity comparisons, and other natural language understanding tasks. Embeddings convert text into high-dimensional numerical vectors that capture semantic meaning, enabling mathematical operations on text. + +## Capabilities + +### Embed Content + +Generate embeddings for text content. Supports single or multiple content inputs and various embedding tasks including retrieval, classification, and semantic similarity. + +```python { .api } +def embed_content( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[EmbedContentConfig] = None +) -> EmbedContentResponse: + """ + Generate embeddings for content. + + Parameters: + model (str): Model identifier for embeddings (e.g., 'text-embedding-004', + 'text-multilingual-embedding-002'). Use models optimized for embeddings. + contents (Union[str, list[Content], Content]): Content to embed. Can be: + - str: Simple text to embed + - Content: Content object with text parts + - list[Content]: Multiple content objects to embed in batch + config (EmbedContentConfig, optional): Embedding configuration including: + - task_type: Type of embedding task (retrieval, classification, etc.) + - title: Document title for RETRIEVAL_DOCUMENT task + - output_dimensionality: Desired embedding dimension (model-dependent) + + Returns: + EmbedContentResponse: Response containing embeddings and metadata. + Each embedding is a list of float values representing the vector. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def embed_content( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[EmbedContentConfig] = None +) -> EmbedContentResponse: + """Async version of embed_content.""" + ... +``` + +**Usage Example - Simple Embedding:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Generate embedding for a single text +response = client.models.embed_content( + model='text-embedding-004', + contents='What is the capital of France?' +) + +# Access the embedding vector +embedding = response.embeddings[0].values +print(f"Embedding dimension: {len(embedding)}") +print(f"First 5 values: {embedding[:5]}") +``` + +**Usage Example - Batch Embeddings:** + +```python +from google.genai import Client +from google.genai.types import Content, Part + +client = Client(api_key='YOUR_API_KEY') + +# Embed multiple texts at once +texts = [ + 'Machine learning is a subset of AI', + 'Deep learning uses neural networks', + 'Natural language processing handles text' +] + +contents = [Content(parts=[Part(text=text)]) for text in texts] + +response = client.models.embed_content( + model='text-embedding-004', + contents=contents +) + +print(f"Generated {len(response.embeddings)} embeddings") +for i, embedding in enumerate(response.embeddings): + print(f"Text {i+1}: dimension={len(embedding.values)}") +``` + +**Usage Example - Semantic Similarity:** + +```python +import numpy as np +from google.genai import Client + +def cosine_similarity(vec1, vec2): + """Calculate cosine similarity between two vectors.""" + dot_product = np.dot(vec1, vec2) + norm1 = np.linalg.norm(vec1) + norm2 = np.linalg.norm(vec2) + return dot_product / (norm1 * norm2) + +client = Client(api_key='YOUR_API_KEY') + +# Embed query and documents +query = "machine learning algorithms" +documents = [ + "Neural networks are a type of machine learning model", + "The weather today is sunny and warm", + "Supervised learning requires labeled training data" +] + +# Get embeddings +query_response = client.models.embed_content( + model='text-embedding-004', + contents=query +) +query_embedding = query_response.embeddings[0].values + +doc_embeddings = [] +for doc in documents: + response = client.models.embed_content( + model='text-embedding-004', + contents=doc + ) + doc_embeddings.append(response.embeddings[0].values) + +# Calculate similarities +for i, doc in enumerate(documents): + similarity = cosine_similarity(query_embedding, doc_embeddings[i]) + print(f"Doc {i+1} similarity: {similarity:.4f}") + print(f" {doc}\n") +``` + +**Usage Example - Task-Specific Embeddings:** + +```python +from google.genai import Client +from google.genai.types import EmbedContentConfig, TaskType + +client = Client(api_key='YOUR_API_KEY') + +# Embed for retrieval query +query_config = EmbedContentConfig( + task_type=TaskType.RETRIEVAL_QUERY +) + +query_response = client.models.embed_content( + model='text-embedding-004', + contents='How does photosynthesis work?', + config=query_config +) + +# Embed documents for retrieval +doc_config = EmbedContentConfig( + task_type=TaskType.RETRIEVAL_DOCUMENT, + title='Biology Textbook Chapter 3' +) + +doc_response = client.models.embed_content( + model='text-embedding-004', + contents='Photosynthesis is the process by which plants convert light energy into chemical energy...', + config=doc_config +) + +print("Query embedding generated") +print("Document embedding generated") +``` + +**Usage Example - Reduced Dimensionality:** + +```python +from google.genai import Client +from google.genai.types import EmbedContentConfig + +client = Client(api_key='YOUR_API_KEY') + +# Generate embeddings with reduced dimensionality for efficiency +config = EmbedContentConfig( + output_dimensionality=256 # Reduce from default dimension +) + +response = client.models.embed_content( + model='text-embedding-004', + contents='Text to embed with reduced dimensions', + config=config +) + +embedding = response.embeddings[0].values +print(f"Embedding dimension: {len(embedding)}") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, TypedDict +from enum import Enum + +# Configuration types +class EmbedContentConfig: + """ + Configuration for embedding generation. + + Attributes: + task_type (TaskType, optional): Type of embedding task. Different tasks may + produce optimized embeddings for specific use cases: + - RETRIEVAL_QUERY: Optimize for search queries + - RETRIEVAL_DOCUMENT: Optimize for searchable documents + - SEMANTIC_SIMILARITY: Optimize for similarity comparisons + - CLASSIFICATION: Optimize for text classification + - CLUSTERING: Optimize for clustering tasks + - QUESTION_ANSWERING: Optimize for QA tasks + - FACT_VERIFICATION: Optimize for fact checking + title (str, optional): Document title, used with RETRIEVAL_DOCUMENT task + to provide context for the embedding. + output_dimensionality (int, optional): Desired output dimension for the embedding. + Some models support dimensionality reduction. Smaller dimensions can reduce + storage and computation costs. Check model documentation for supported values. + """ + task_type: Optional[TaskType] = None + title: Optional[str] = None + output_dimensionality: Optional[int] = None + +class TaskType(Enum): + """Embedding task types for optimization.""" + TASK_TYPE_UNSPECIFIED = 'TASK_TYPE_UNSPECIFIED' + RETRIEVAL_QUERY = 'RETRIEVAL_QUERY' + RETRIEVAL_DOCUMENT = 'RETRIEVAL_DOCUMENT' + SEMANTIC_SIMILARITY = 'SEMANTIC_SIMILARITY' + CLASSIFICATION = 'CLASSIFICATION' + CLUSTERING = 'CLUSTERING' + QUESTION_ANSWERING = 'QUESTION_ANSWERING' + FACT_VERIFICATION = 'FACT_VERIFICATION' + CODE_RETRIEVAL_QUERY = 'CODE_RETRIEVAL_QUERY' + +# Response types +class EmbedContentResponse: + """ + Response from embedding generation. + + Attributes: + embeddings (list[ContentEmbedding]): List of embeddings, one for each input content. + Each embedding contains the vector values and optional statistics. + metadata (EmbedContentMetadata, optional): Metadata about the embedding operation. + """ + embeddings: list[ContentEmbedding] + metadata: Optional[EmbedContentMetadata] = None + +class ContentEmbedding: + """ + Individual content embedding. + + Attributes: + values (list[float]): Embedding vector as a list of float values. The length + depends on the model and optional output_dimensionality configuration. + Typical dimensions: 768, 1024, 1536, or custom reduced dimensions. + statistics (ContentEmbeddingStatistics, optional): Statistics about the embedding. + """ + values: list[float] + statistics: Optional[ContentEmbeddingStatistics] = None + +class ContentEmbeddingStatistics: + """ + Statistics about an embedding. + + Attributes: + token_count (int, optional): Number of tokens in the input content. + truncated (bool, optional): Whether the input was truncated to fit model limits. + """ + token_count: Optional[int] = None + truncated: Optional[bool] = None + +class EmbedContentMetadata: + """ + Metadata about the embedding operation. + + Attributes: + model_version (str, optional): Version of the model used for embedding. + """ + model_version: Optional[str] = None + +# Content types (shared with other capabilities) +class Content: + """ + Container for content with role and parts. + + Attributes: + parts (list[Part]): List of content parts + role (str, optional): Role ('user' or 'model') + """ + parts: list[Part] + role: Optional[str] = None + +class Part: + """ + Individual content part. + + For embeddings, typically only text parts are used. + + Attributes: + text (str, optional): Text content to embed + inline_data (Blob, optional): Inline binary data (rarely used for embeddings) + file_data (FileData, optional): Reference to file (rarely used for embeddings) + """ + text: Optional[str] = None + inline_data: Optional[Blob] = None + file_data: Optional[FileData] = None + +class Blob: + """ + Binary data with MIME type. + + Attributes: + mime_type (str): MIME type + data (bytes): Binary data + """ + mime_type: str + data: bytes + +class FileData: + """ + Reference to uploaded file. + + Attributes: + file_uri (str): URI of uploaded file + mime_type (str): MIME type + """ + file_uri: str + mime_type: str + +# TypedDict variants for flexible usage +class EmbedContentConfigDict(TypedDict, total=False): + """TypedDict variant of EmbedContentConfig.""" + task_type: TaskType + title: str + output_dimensionality: int +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md b/.tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md new file mode 100644 index 000000000..0e81161f8 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md @@ -0,0 +1,520 @@ +# File Search Stores and Documents + +Create and manage file search stores with document retrieval for retrieval-augmented generation (RAG). File search stores enable semantic search over your documents, allowing models to ground responses in your own data sources. + +## Capabilities + +### Create File Search Store + +Create a file search store to organize and search documents. + +```python { .api } +class FileSearchStores: + """Synchronous file search stores API.""" + + def create( + self, + *, + config: CreateFileSearchStoreConfig + ) -> FileSearchStore: + """ + Create a file search store. + + Parameters: + config (CreateFileSearchStoreConfig): Store configuration including: + - display_name: Display name for the store + - description: Description of the store + + Returns: + FileSearchStore: Created store with name and metadata. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + + @property + def documents(self) -> Documents: + """Access documents sub-API for managing documents within stores.""" + ... + +class AsyncFileSearchStores: + """Asynchronous file search stores API.""" + + async def create( + self, + *, + config: CreateFileSearchStoreConfig + ) -> FileSearchStore: + """Async version of create.""" + ... + + @property + def documents(self) -> AsyncDocuments: + """Access async documents sub-API.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import CreateFileSearchStoreConfig + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +config = CreateFileSearchStoreConfig( + display_name='Product Documentation', + description='Store for product manuals and guides' +) + +store = client.file_search_stores.create(config=config) +print(f"Created store: {store.name}") +``` + +### Get File Search Store + +Retrieve information about a file search store. + +```python { .api } +class FileSearchStores: + """Synchronous file search stores API.""" + + def get(self, *, name: str) -> FileSearchStore: + """ + Get file search store information. + + Parameters: + name (str): Store name in format 'fileSearchStores/*'. + + Returns: + FileSearchStore: Store information. + """ + ... + +class AsyncFileSearchStores: + """Asynchronous file search stores API.""" + + async def get(self, *, name: str) -> FileSearchStore: + """Async version of get.""" + ... +``` + +### Delete File Search Store + +Delete a file search store and all its documents. + +```python { .api } +class FileSearchStores: + """Synchronous file search stores API.""" + + def delete(self, *, name: str) -> None: + """ + Delete a file search store. + + Parameters: + name (str): Store name in format 'fileSearchStores/*'. + """ + ... + +class AsyncFileSearchStores: + """Asynchronous file search stores API.""" + + async def delete(self, *, name: str) -> None: + """Async version of delete.""" + ... +``` + +### Import File + +Import a file into a file search store from GCS. + +```python { .api } +class FileSearchStores: + """Synchronous file search stores API.""" + + def import_file( + self, + *, + store: str, + source: ImportFileSource, + config: Optional[ImportFileConfig] = None + ) -> ImportFileOperation: + """ + Import file into store (returns long-running operation). + + Parameters: + store (str): Store name. + source (ImportFileSource): Import source (GCS URI). + config (ImportFileConfig, optional): Import configuration. + + Returns: + ImportFileOperation: Long-running operation for import. + """ + ... + +class AsyncFileSearchStores: + """Asynchronous file search stores API.""" + + async def import_file( + self, + *, + store: str, + source: ImportFileSource, + config: Optional[ImportFileConfig] = None + ) -> ImportFileOperation: + """Async version of import_file.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import ImportFileSource + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +source = ImportFileSource( + gcs_uri='gs://my-bucket/document.pdf' +) + +operation = client.file_search_stores.import_file( + store='fileSearchStores/abc123', + source=source +) + +# Poll for completion +while not operation.done: + import time + time.sleep(5) + operation = client.operations.get(operation) + +print("Import complete") +``` + +### Upload to File Search Store + +Upload a local file directly to a file search store. + +```python { .api } +class FileSearchStores: + """Synchronous file search stores API.""" + + def upload_to_file_search_store( + self, + *, + store: str, + file: Union[str, Path, IO], + config: Optional[UploadToFileSearchStoreConfig] = None + ) -> UploadToFileSearchStoreOperation: + """ + Upload file to store (returns long-running operation). + + Parameters: + store (str): Store name. + file (Union[str, Path, IO]): File to upload. + config (UploadToFileSearchStoreConfig, optional): Upload configuration including: + - mime_type: File MIME type + - display_name: Display name + + Returns: + UploadToFileSearchStoreOperation: Long-running operation for upload. + """ + ... + +class AsyncFileSearchStores: + """Asynchronous file search stores API.""" + + async def upload_to_file_search_store( + self, + *, + store: str, + file: Union[str, Path, IO], + config: Optional[UploadToFileSearchStoreConfig] = None + ) -> UploadToFileSearchStoreOperation: + """Async version of upload_to_file_search_store.""" + ... +``` + +### List File Search Stores + +List all file search stores. + +```python { .api } +class FileSearchStores: + """Synchronous file search stores API.""" + + def list( + self, + *, + config: Optional[ListFileSearchStoresConfig] = None + ) -> Union[Pager[FileSearchStore], Iterator[FileSearchStore]]: + """ + List file search stores. + + Parameters: + config (ListFileSearchStoresConfig, optional): List configuration. + + Returns: + Union[Pager[FileSearchStore], Iterator[FileSearchStore]]: Paginated store list. + """ + ... + +class AsyncFileSearchStores: + """Asynchronous file search stores API.""" + + async def list( + self, + *, + config: Optional[ListFileSearchStoresConfig] = None + ) -> Union[AsyncPager[FileSearchStore], AsyncIterator[FileSearchStore]]: + """Async version of list.""" + ... +``` + +### Documents Sub-API + +Manage documents within file search stores. + +```python { .api } +class Documents: + """Synchronous documents API.""" + + def get(self, *, name: str) -> Document: + """ + Get document information. + + Parameters: + name (str): Document name in format 'fileSearchStores/*/documents/*'. + + Returns: + Document: Document information. + """ + ... + + def delete(self, *, name: str) -> None: + """ + Delete a document. + + Parameters: + name (str): Document name. + """ + ... + + def list( + self, + *, + parent: str, + config: Optional[ListDocumentsConfig] = None + ) -> Union[Pager[Document], Iterator[Document]]: + """ + List documents in a store. + + Parameters: + parent (str): Store name in format 'fileSearchStores/*'. + config (ListDocumentsConfig, optional): List configuration. + + Returns: + Union[Pager[Document], Iterator[Document]]: Paginated document list. + """ + ... + +class AsyncDocuments: + """Asynchronous documents API.""" + + async def get(self, *, name: str) -> Document: + """Async version of get.""" + ... + + async def delete(self, *, name: str) -> None: + """Async version of delete.""" + ... + + async def list( + self, + *, + parent: str, + config: Optional[ListDocumentsConfig] = None + ) -> Union[AsyncPager[Document], AsyncIterator[Document]]: + """Async version of list.""" + ... +``` + +**Usage Example - Using File Search in RAG:** + +```python +from google.genai import Client +from google.genai.types import ( + GenerateContentConfig, + Tool, + FileSearch +) + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Configure file search tool +config = GenerateContentConfig( + tools=[Tool(file_search=FileSearch( + file_search_store='fileSearchStores/abc123' + ))] +) + +# Generate with grounding in documents +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='What is the return policy for damaged items?', + config=config +) + +print(response.text) + +# Check grounding metadata +if response.candidates[0].grounding_metadata: + print("Grounded in store documents") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, Iterator, AsyncIterator, IO, TypedDict +from pathlib import Path +from datetime import datetime +from enum import Enum + +# Configuration types +class CreateFileSearchStoreConfig: + """ + Configuration for creating file search store. + + Attributes: + display_name (str, optional): Display name. + description (str, optional): Description. + """ + display_name: Optional[str] = None + description: Optional[str] = None + +class ImportFileConfig: + """Configuration for importing files.""" + display_name: Optional[str] = None + +class UploadToFileSearchStoreConfig: + """ + Configuration for uploading files. + + Attributes: + mime_type (str, optional): MIME type. + display_name (str, optional): Display name. + """ + mime_type: Optional[str] = None + display_name: Optional[str] = None + +class ListFileSearchStoresConfig: + """Configuration for listing stores.""" + page_size: Optional[int] = None + page_token: Optional[str] = None + +class ListDocumentsConfig: + """Configuration for listing documents.""" + page_size: Optional[int] = None + page_token: Optional[str] = None + +# Response types +class FileSearchStore: + """ + File search store information. + + Attributes: + name (str): Store resource name. + display_name (str, optional): Display name. + description (str, optional): Description. + create_time (datetime): Creation time. + update_time (datetime): Last update time. + """ + name: str + display_name: Optional[str] = None + description: Optional[str] = None + create_time: datetime + update_time: datetime + +class Document: + """ + Document in file search store. + + Attributes: + name (str): Document resource name. + display_name (str, optional): Display name. + mime_type (str): MIME type. + size_bytes (int): Size in bytes. + state (DocumentState): Processing state. + create_time (datetime): Creation time. + update_time (datetime): Last update time. + """ + name: str + display_name: Optional[str] = None + mime_type: str + size_bytes: int + state: DocumentState + create_time: datetime + update_time: datetime + +class DocumentState(Enum): + """Document processing states.""" + STATE_UNSPECIFIED = 'STATE_UNSPECIFIED' + PROCESSING = 'PROCESSING' + ACTIVE = 'ACTIVE' + FAILED = 'FAILED' + +class ImportFileSource: + """ + Import source. + + Attributes: + gcs_uri (str): GCS URI of file to import. + """ + gcs_uri: str + +# Operation types +class ImportFileOperation: + """Long-running operation for file import.""" + name: str + done: bool + error: Optional[OperationError] = None + response: Optional[dict] = None + +class UploadToFileSearchStoreOperation: + """Long-running operation for file upload.""" + name: str + done: bool + error: Optional[OperationError] = None + response: Optional[dict] = None + +class OperationError: + """Operation error.""" + code: int + message: str + +# Tool types for RAG +class FileSearch: + """ + File search tool configuration. + + Attributes: + file_search_store (str, optional): Store name to search. + """ + file_search_store: Optional[str] = None + +# Pager types +class Pager[T]: + """Synchronous pager.""" + page: list[T] + def next_page(self) -> None: ... + def __iter__(self) -> Iterator[T]: ... + +class AsyncPager[T]: + """Asynchronous pager.""" + page: list[T] + async def next_page(self) -> None: ... + async def __aiter__(self) -> AsyncIterator[T]: ... +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/files.md b/.tessl/tiles/tessl/pypi-google-genai/docs/files.md new file mode 100644 index 000000000..5298d9ebc --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/files.md @@ -0,0 +1,450 @@ +# File Management + +Upload, manage, and download files for use with multimodal content generation. File management is available in the Gemini Developer API only. Files can be images, audio, video, or documents that are referenced in content generation requests. + +## Capabilities + +### Upload File + +Upload a file to be used in content generation. Files are stored temporarily and automatically deleted after a retention period. + +```python { .api } +class Files: + """Synchronous file management API.""" + + def upload( + self, + *, + file: Union[str, Path, IO], + config: Optional[UploadFileConfig] = None + ) -> File: + """ + Upload a file for use in generation requests. + + Parameters: + file (Union[str, Path, IO]): File to upload. Can be: + - str or Path: File path to upload + - IO: File-like object (must be opened in binary mode) + config (UploadFileConfig, optional): Upload configuration including: + - mime_type: MIME type of the file (auto-detected if not provided) + - display_name: Display name for the file + + Returns: + File: Uploaded file information including URI, name, and metadata. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncFiles: + """Asynchronous file management API.""" + + async def upload( + self, + *, + file: Union[str, Path, IO], + config: Optional[UploadFileConfig] = None + ) -> File: + """Async version of upload.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import UploadFileConfig + +client = Client(api_key='YOUR_API_KEY') + +# Upload from file path +config = UploadFileConfig( + mime_type='image/jpeg', + display_name='Product Photo' +) + +file = client.files.upload( + file='product.jpg', + config=config +) + +print(f"Uploaded file: {file.name}") +print(f"URI: {file.uri}") +print(f"State: {file.state}") + +# Use in generation +from google.genai.types import Content, Part, FileData + +content = Content( + parts=[ + Part(text='Describe this image'), + Part(file_data=FileData(file_uri=file.uri, mime_type=file.mime_type)) + ] +) + +response = client.models.generate_content( + model='gemini-2.0-flash', + contents=content +) +print(response.text) +``` + +### Get File + +Retrieve information about an uploaded file. + +```python { .api } +class Files: + """Synchronous file management API.""" + + def get(self, *, name: str) -> File: + """ + Get file information. + + Parameters: + name (str): File name (from File.name) in format 'files/{file_id}'. + + Returns: + File: File information including state, size, and metadata. + + Raises: + ClientError: For client errors including 404 if file not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncFiles: + """Asynchronous file management API.""" + + async def get(self, *, name: str) -> File: + """Async version of get.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Get file info +file = client.files.get(name='files/abc123') + +print(f"Name: {file.display_name}") +print(f"Size: {file.size_bytes} bytes") +print(f"State: {file.state}") +print(f"Created: {file.create_time}") +print(f"Expires: {file.expiration_time}") +``` + +### Delete File + +Delete an uploaded file. Files are automatically deleted after their expiration time, but you can delete them earlier if needed. + +```python { .api } +class Files: + """Synchronous file management API.""" + + def delete(self, *, name: str) -> None: + """ + Delete a file. + + Parameters: + name (str): File name in format 'files/{file_id}'. + + Raises: + ClientError: For client errors including 404 if file not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncFiles: + """Asynchronous file management API.""" + + async def delete(self, *, name: str) -> None: + """Async version of delete.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Delete file +client.files.delete(name='files/abc123') +print("File deleted") +``` + +### Download File + +Download the contents of an uploaded file. + +```python { .api } +class Files: + """Synchronous file management API.""" + + def download(self, *, name: str, path: Optional[str] = None) -> bytes: + """ + Download file contents. + + Parameters: + name (str): File name in format 'files/{file_id}'. + path (str, optional): Local file path to save to. If not provided, + returns file contents as bytes without saving. + + Returns: + bytes: File contents. If path is provided, also saves to file. + + Raises: + ClientError: For client errors including 404 if file not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncFiles: + """Asynchronous file management API.""" + + async def download(self, *, name: str, path: Optional[str] = None) -> bytes: + """Async version of download.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Download to file +data = client.files.download(name='files/abc123', path='downloaded.jpg') +print(f"Downloaded {len(data)} bytes") + +# Download to memory +data = client.files.download(name='files/abc123') +# Process data in memory +``` + +### List Files + +List all uploaded files with optional pagination. + +```python { .api } +class Files: + """Synchronous file management API.""" + + def list( + self, + *, + config: Optional[ListFilesConfig] = None + ) -> Union[Pager[File], Iterator[File]]: + """ + List uploaded files. + + Parameters: + config (ListFilesConfig, optional): List configuration including: + - page_size: Number of files per page (default: 50, max: 100) + - page_token: Token for pagination + + Returns: + Union[Pager[File], Iterator[File]]: Paginated file list. If page_size + is set, returns Pager for manual pagination. Otherwise, returns + Iterator that automatically handles pagination. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncFiles: + """Asynchronous file management API.""" + + async def list( + self, + *, + config: Optional[ListFilesConfig] = None + ) -> Union[AsyncPager[File], AsyncIterator[File]]: + """Async version of list.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# List all files (auto-pagination) +for file in client.files.list(): + print(f"{file.display_name}: {file.name} ({file.state})") + +# Manual pagination +from google.genai.types import ListFilesConfig + +config = ListFilesConfig(page_size=10) +pager = client.files.list(config=config) + +print(f"Page 1: {len(pager.page)} files") +for file in pager.page: + print(f" {file.display_name}") + +# Get next page +pager.next_page() +print(f"Page 2: {len(pager.page)} files") +``` + +## Types + +```python { .api } +from typing import Optional, Union, IO, Iterator, AsyncIterator +from pathlib import Path +from enum import Enum +from datetime import datetime + +# Configuration types +class UploadFileConfig: + """ + Configuration for file upload. + + Attributes: + mime_type (str, optional): MIME type of the file. If not provided, will be + auto-detected from file extension or content. + display_name (str, optional): Human-readable display name for the file. + """ + mime_type: Optional[str] = None + display_name: Optional[str] = None + +class ListFilesConfig: + """ + Configuration for listing files. + + Attributes: + page_size (int, optional): Number of files per page (1-100). Default: 50. + page_token (str, optional): Token from previous response for pagination. + """ + page_size: Optional[int] = None + page_token: Optional[str] = None + +# Response types +class File: + """ + Uploaded file information. + + Attributes: + name (str): File resource name in format 'files/{file_id}'. + display_name (str, optional): Display name for the file. + mime_type (str): MIME type of the file. + size_bytes (int): File size in bytes. + create_time (datetime): When file was created. + update_time (datetime): When file was last updated. + expiration_time (datetime): When file will be automatically deleted. + sha256_hash (bytes): SHA256 hash of file contents. + uri (str): URI for referencing the file in API requests. + state (FileState): Current processing state of the file. + error (FileError, optional): Error if file processing failed. + """ + name: str + display_name: Optional[str] = None + mime_type: str + size_bytes: int + create_time: datetime + update_time: datetime + expiration_time: datetime + sha256_hash: bytes + uri: str + state: FileState + error: Optional[FileError] = None + +class FileState(Enum): + """File processing state.""" + STATE_UNSPECIFIED = 'STATE_UNSPECIFIED' + PROCESSING = 'PROCESSING' + ACTIVE = 'ACTIVE' + FAILED = 'FAILED' + +class FileError: + """ + File processing error. + + Attributes: + code (int): Error code. + message (str): Error message. + """ + code: int + message: str + +class FileData: + """ + Reference to uploaded file for use in content. + + Attributes: + file_uri (str): URI from File.uri. + mime_type (str): MIME type from File.mime_type. + """ + file_uri: str + mime_type: str + +# Pager types +class Pager[T]: + """ + Synchronous pager for paginated results. + + Attributes: + page (list[T]): Current page of items. + page_size (int): Number of items per page. + + Methods: + next_page(): Fetch next page of results. + """ + page: list[T] + page_size: int + + def next_page(self) -> None: + """Fetch next page and update self.page.""" + ... + + def __iter__(self) -> Iterator[T]: + """Iterate over all items across pages.""" + ... + +class AsyncPager[T]: + """ + Asynchronous pager for paginated results. + + Attributes: + page (list[T]): Current page of items. + page_size (int): Number of items per page. + + Methods: + next_page(): Fetch next page of results. + """ + page: list[T] + page_size: int + + async def next_page(self) -> None: + """Fetch next page and update self.page.""" + ... + + async def __aiter__(self) -> AsyncIterator[T]: + """Async iterate over all items across pages.""" + ... + +# TypedDict variants +class UploadFileConfigDict(TypedDict, total=False): + """TypedDict variant of UploadFileConfig.""" + mime_type: str + display_name: str + +class ListFilesConfigDict(TypedDict, total=False): + """TypedDict variant of ListFilesConfig.""" + page_size: int + page_token: str +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md b/.tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md new file mode 100644 index 000000000..ce9e86ea3 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md @@ -0,0 +1,551 @@ +# Image Generation + +Generate, edit, upscale, and segment images using Imagen models. Provides comprehensive image generation and manipulation capabilities including text-to-image generation, image editing with masks, upscaling for enhanced resolution, image recontextualization, and semantic segmentation. + +## Capabilities + +### Generate Images + +Generate images from text prompts using Imagen models. Supports various aspect ratios, safety filtering, and configuration options. + +```python { .api } +def generate_images( + *, + model: str, + prompt: str, + config: Optional[GenerateImagesConfig] = None +) -> GenerateImagesResponse: + """ + Generate images from a text prompt. + + Parameters: + model (str): Model identifier (e.g., 'imagen-3.0-generate-001', 'imagen-3.0-fast-generate-001'). + prompt (str): Text description of the image to generate. More detailed prompts + typically produce better results. + config (GenerateImagesConfig, optional): Generation configuration including: + - number_of_images: Number of images to generate (1-8) + - aspect_ratio: Image aspect ratio ('1:1', '16:9', '9:16', '4:3', '3:4') + - negative_prompt: What to avoid in the image + - safety_filter_level: Safety filtering level + - person_generation: Control person generation in images + - language: Language of the prompt + + Returns: + GenerateImagesResponse: Response containing generated images and metadata. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def generate_images( + *, + model: str, + prompt: str, + config: Optional[GenerateImagesConfig] = None +) -> GenerateImagesResponse: + """Async version of generate_images.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import GenerateImagesConfig + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +config = GenerateImagesConfig( + number_of_images=4, + aspect_ratio='16:9', + negative_prompt='blurry, low quality', + safety_filter_level='block_some' +) + +response = client.models.generate_images( + model='imagen-3.0-generate-001', + prompt='A serene mountain landscape at sunset with a lake', + config=config +) + +for i, image in enumerate(response.generated_images): + image.pil_image.save(f'generated_{i}.png') +``` + +### Edit Image + +Edit existing images using text prompts and optional masks to specify editing regions. + +```python { .api } +def edit_image( + *, + model: str, + prompt: str, + reference_images: Sequence[ReferenceImage], + config: Optional[EditImageConfig] = None +) -> EditImageResponse: + """ + Edit an image using a text prompt and reference images. + + Parameters: + model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). + prompt (str): Text description of desired edits. + reference_images (Sequence[ReferenceImage]): Reference images for editing. + Can include base image, mask image, and control images. + config (EditImageConfig, optional): Editing configuration including: + - number_of_images: Number of edited variations + - negative_prompt: What to avoid + - edit_mode: Editing mode (INPAINT, OUTPAINT, etc.) + - mask_mode: Mask interpretation mode + - mask_dilation: Mask dilation amount + - guidance_scale: Prompt adherence strength + - safety_filter_level: Safety filtering + + Returns: + EditImageResponse: Response containing edited images. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def edit_image( + *, + model: str, + prompt: str, + reference_images: Sequence[ReferenceImage], + config: Optional[EditImageConfig] = None +) -> EditImageResponse: + """Async version of edit_image.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import ( + EditImageConfig, + ReferenceImage, + Image, + EditMode +) + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Load images +base_image = Image.from_file('photo.jpg') +mask_image = Image.from_file('mask.png') # White areas will be edited + +reference_images = [ + ReferenceImage(reference_type='reference_image', reference_image=base_image), + ReferenceImage(reference_type='mask_image', reference_image=mask_image) +] + +config = EditImageConfig( + number_of_images=2, + edit_mode=EditMode.INPAINT_INSERTION, + negative_prompt='distorted, blurry' +) + +response = client.models.edit_image( + model='imagen-3.0-capability-001', + prompt='A red sports car', + reference_images=reference_images, + config=config +) + +for i, image in enumerate(response.generated_images): + image.pil_image.save(f'edited_{i}.png') +``` + +### Upscale Image + +Upscale images to higher resolutions while preserving quality and details. + +```python { .api } +def upscale_image( + *, + model: str, + image: Image, + upscale_factor: str, + config: Optional[UpscaleImageConfig] = None +) -> UpscaleImageResponse: + """ + Upscale an image to higher resolution. + + Parameters: + model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). + image (Image): Image to upscale. + upscale_factor (str): Upscaling factor ('x2' or 'x4'). + config (UpscaleImageConfig, optional): Upscaling configuration including: + - number_of_images: Number of upscaled variations + - safety_filter_level: Safety filtering level + + Returns: + UpscaleImageResponse: Response containing upscaled images. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def upscale_image( + *, + model: str, + image: Image, + upscale_factor: str, + config: Optional[UpscaleImageConfig] = None +) -> UpscaleImageResponse: + """Async version of upscale_image.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import Image, UpscaleImageConfig + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +image = Image.from_file('low_res.jpg') + +config = UpscaleImageConfig( + number_of_images=1 +) + +response = client.models.upscale_image( + model='imagen-3.0-capability-001', + image=image, + upscale_factor='x4', + config=config +) + +response.generated_images[0].pil_image.save('upscaled_4x.png') +``` + +### Recontext Image + +Recontextualize product images by changing their backgrounds or settings while preserving the main subject. + +```python { .api } +def recontext_image( + *, + model: str, + prompt: str, + source: RecontextImageSource, + config: Optional[RecontextImageConfig] = None +) -> RecontextImageResponse: + """ + Recontextualize a product image with a new background. + + Parameters: + model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). + prompt (str): Description of the new context/background. + source (RecontextImageSource): Source containing product image and optional mask. + config (RecontextImageConfig, optional): Configuration including: + - number_of_images: Number of variations + - negative_prompt: What to avoid + - safety_filter_level: Safety filtering + + Returns: + RecontextImageResponse: Response with recontextualized images. + """ + ... + +async def recontext_image( + *, + model: str, + prompt: str, + source: RecontextImageSource, + config: Optional[RecontextImageConfig] = None +) -> RecontextImageResponse: + """Async version of recontext_image.""" + ... +``` + +### Segment Image + +Perform semantic segmentation on images to identify and isolate different objects and regions. + +```python { .api } +def segment_image( + *, + model: str, + source: SegmentImageSource, + config: Optional[SegmentImageConfig] = None +) -> SegmentImageResponse: + """ + Segment an image to identify objects and regions. + + Parameters: + model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). + source (SegmentImageSource): Source image and optional entity labels. + config (SegmentImageConfig, optional): Segmentation configuration including: + - segment_mode: Segmentation mode (FOREGROUND, ENTITY) + + Returns: + SegmentImageResponse: Response with segmentation masks. + """ + ... + +async def segment_image( + *, + model: str, + source: SegmentImageSource, + config: Optional[SegmentImageConfig] = None +) -> SegmentImageResponse: + """Async version of segment_image.""" + ... +``` + +## Types + +```python { .api } +from typing import Optional, List, Sequence, Literal +from enum import Enum +import PIL.Image + +# Configuration types +class GenerateImagesConfig: + """ + Configuration for image generation. + + Attributes: + number_of_images (int, optional): Number of images to generate (1-8). Default: 1. + aspect_ratio (str, optional): Aspect ratio ('1:1', '16:9', '9:16', '4:3', '3:4'). Default: '1:1'. + negative_prompt (str, optional): What to avoid in generated images. + safety_filter_level (str, optional): Safety filtering ('block_most', 'block_some', 'block_few'). + person_generation (PersonGeneration, optional): Control person generation. + include_safety_attributes (bool, optional): Include safety attributes in response. + language (str, optional): Language of the prompt. + add_watermark (bool, optional): Add watermark to images. + """ + number_of_images: Optional[int] = None + aspect_ratio: Optional[str] = None + negative_prompt: Optional[str] = None + safety_filter_level: Optional[str] = None + person_generation: Optional[PersonGeneration] = None + include_safety_attributes: Optional[bool] = None + language: Optional[str] = None + add_watermark: Optional[bool] = None + +class EditImageConfig: + """ + Configuration for image editing. + + Attributes: + number_of_images (int, optional): Number of edited variations. + negative_prompt (str, optional): What to avoid. + edit_mode (EditMode, optional): Editing mode. + mask_mode (MaskMode, optional): Mask interpretation. + mask_dilation (float, optional): Mask dilation amount. + guidance_scale (float, optional): Prompt adherence (1-20). + safety_filter_level (str, optional): Safety filtering. + person_generation (PersonGeneration, optional): Person generation control. + include_safety_attributes (bool, optional): Include safety attributes. + """ + number_of_images: Optional[int] = None + negative_prompt: Optional[str] = None + edit_mode: Optional[EditMode] = None + mask_mode: Optional[MaskMode] = None + mask_dilation: Optional[float] = None + guidance_scale: Optional[float] = None + safety_filter_level: Optional[str] = None + person_generation: Optional[PersonGeneration] = None + include_safety_attributes: Optional[bool] = None + +class UpscaleImageConfig: + """ + Configuration for image upscaling. + + Attributes: + number_of_images (int, optional): Number of upscaled variations. + safety_filter_level (str, optional): Safety filtering. + include_safety_attributes (bool, optional): Include safety attributes. + """ + number_of_images: Optional[int] = None + safety_filter_level: Optional[str] = None + include_safety_attributes: Optional[bool] = None + +class RecontextImageConfig: + """Configuration for image recontextualization.""" + number_of_images: Optional[int] = None + negative_prompt: Optional[str] = None + safety_filter_level: Optional[str] = None + include_safety_attributes: Optional[bool] = None + +class SegmentImageConfig: + """ + Configuration for image segmentation. + + Attributes: + segment_mode (SegmentMode, optional): Segmentation mode. + """ + segment_mode: Optional[SegmentMode] = None + +# Response types +class GenerateImagesResponse: + """ + Response from image generation. + + Attributes: + generated_images (list[GeneratedImage]): Generated images with metadata. + rai_filtered_reason (str, optional): Reason if filtered by safety. + """ + generated_images: list[GeneratedImage] + rai_filtered_reason: Optional[str] = None + +class EditImageResponse: + """Response from image editing.""" + generated_images: list[GeneratedImage] + rai_filtered_reason: Optional[str] = None + +class UpscaleImageResponse: + """Response from image upscaling.""" + generated_images: list[GeneratedImage] + rai_filtered_reason: Optional[str] = None + +class RecontextImageResponse: + """Response from image recontextualization.""" + generated_images: list[GeneratedImage] + rai_filtered_reason: Optional[str] = None + +class SegmentImageResponse: + """ + Response from image segmentation. + + Attributes: + generated_masks (list[GeneratedImageMask]): Segmentation masks. + """ + generated_masks: list[GeneratedImageMask] + +class GeneratedImage: + """ + Generated or edited image. + + Attributes: + image (Image): Image object. + pil_image (PIL.Image.Image): PIL Image for easy manipulation. + generation_seed (int, optional): Seed used for generation. + rai_info (str, optional): RAI information. + safety_attributes (SafetyAttributes, optional): Safety attributes. + """ + image: Image + pil_image: PIL.Image.Image + generation_seed: Optional[int] = None + rai_info: Optional[str] = None + safety_attributes: Optional[SafetyAttributes] = None + +class GeneratedImageMask: + """ + Segmentation mask. + + Attributes: + mask_image (Image): Mask image. + entity_label (EntityLabel, optional): Entity label. + """ + mask_image: Image + entity_label: Optional[EntityLabel] = None + +# Input types +class ReferenceImage: + """ + Reference image for editing. + + Attributes: + reference_type (str): Type ('reference_image', 'mask_image', 'control_image', etc.). + reference_image (Image): The reference image. + reference_id (int, optional): Reference identifier. + """ + reference_type: str + reference_image: Image + reference_id: Optional[int] = None + +class RecontextImageSource: + """ + Source for recontextualization. + + Attributes: + product_image (Image): Product image. + product_mask (Image, optional): Product mask. + """ + product_image: Image + product_mask: Optional[Image] = None + +class SegmentImageSource: + """ + Source for segmentation. + + Attributes: + image (Image): Image to segment. + entity_labels (list[EntityLabel], optional): Entity labels to identify. + """ + image: Image + entity_labels: Optional[list[EntityLabel]] = None + +class Image: + """ + Image data supporting multiple formats. + + Can be created from: + - File: Image.from_file('path.jpg') + - URL: Image.from_url('https://...') + - Bytes: Image.from_bytes(data, mime_type='image/jpeg') + - PIL: Image.from_pil(pil_image) + """ + @staticmethod + def from_file(path: str) -> Image: ... + + @staticmethod + def from_url(url: str) -> Image: ... + + @staticmethod + def from_bytes(data: bytes, mime_type: str) -> Image: ... + + @staticmethod + def from_pil(pil_image: PIL.Image.Image) -> Image: ... + +# Enum types +class PersonGeneration(Enum): + """Person generation control.""" + DONT_ALLOW = 'dont_allow' + ALLOW_ADULT = 'allow_adult' + ALLOW_ALL = 'allow_all' + +class EditMode(Enum): + """Image editing modes.""" + EDIT_MODE_UNSPECIFIED = 'EDIT_MODE_UNSPECIFIED' + INPAINT_INSERTION = 'INPAINT_INSERTION' + INPAINT_REMOVAL = 'INPAINT_REMOVAL' + OUTPAINT = 'OUTPAINT' + PRODUCT_IMAGE = 'PRODUCT_IMAGE' + +class MaskMode(Enum): + """Mask interpretation modes.""" + MASK_MODE_UNSPECIFIED = 'MASK_MODE_UNSPECIFIED' + MASK_MODE_BACKGROUND = 'MASK_MODE_BACKGROUND' + MASK_MODE_FOREGROUND = 'MASK_MODE_FOREGROUND' + MASK_MODE_SEMANTIC = 'MASK_MODE_SEMANTIC' + +class SegmentMode(Enum): + """Segmentation modes.""" + SEGMENT_MODE_UNSPECIFIED = 'SEGMENT_MODE_UNSPECIFIED' + FOREGROUND = 'FOREGROUND' + ENTITY = 'ENTITY' + +class SafetyAttributes: + """Safety attributes for images.""" + blocked: bool + scores: dict[str, float] + +class EntityLabel: + """Entity label for segmentation.""" + label: str + score: Optional[float] = None +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/index.md b/.tessl/tiles/tessl/pypi-google-genai/docs/index.md new file mode 100644 index 000000000..10ab2877a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/index.md @@ -0,0 +1,599 @@ +# Google GenAI Python SDK + +A comprehensive Python SDK for integrating Google's generative AI models into applications, supporting both the Gemini Developer API and Vertex AI APIs. The SDK provides a unified interface for content generation, multi-turn conversations, embeddings, image and video generation, file management, caching, batch processing, fine-tuning, and real-time bidirectional streaming. + +## Package Information + +- **Package Name**: google-genai +- **Language**: Python +- **Installation**: `pip install google-genai` +- **Version**: 1.51.0 + +## Core Imports + +```python +from google import genai +``` + +Common imports: + +```python +from google.genai import Client +from google.genai import types +``` + +## Basic Usage + +```python +from google.genai import Client + +# Initialize client with API key (Gemini Developer API) +client = Client(api_key='YOUR_API_KEY') + +# Generate content +response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Explain how AI works in simple terms.' +) +print(response.text) + +# Multi-turn chat +chat = client.chats.create(model='gemini-2.0-flash') +response = chat.send_message('What is machine learning?') +print(response.text) + +# Close client +client.close() +``` + +With context manager: + +```python +from google.genai import Client + +with Client(api_key='YOUR_API_KEY') as client: + response = client.models.generate_content( + model='gemini-2.0-flash', + contents='Hello!' + ) + print(response.text) +``` + +## Architecture + +The SDK follows a modular architecture with these key components: + +- **Client**: Main entry point providing access to all API modules, supports both Gemini Developer API (api_key) and Vertex AI (credentials/project) +- **API Modules**: Specialized interfaces for models, chats, files, caches, batches, tunings, file_search_stores, operations, live +- **Types System**: Comprehensive type definitions with both Pydantic models and TypedDict variants for flexible usage +- **Async Support**: Complete async/await implementations via the `aio` property on the Client +- **Streaming**: Built-in streaming support for content generation and live interactions +- **Function Calling**: Automatic and manual function calling with Python functions or JSON schemas + +## Capabilities + +### Client Initialization + +Initialize the main client to access all SDK functionality with support for both Gemini Developer API and Vertex AI. + +```python { .api } +class Client: + def __init__( + self, + *, + vertexai: Optional[bool] = None, + api_key: Optional[str] = None, + credentials: Optional[google.auth.credentials.Credentials] = None, + project: Optional[str] = None, + location: Optional[str] = None, + debug_config: Optional[DebugConfig] = None, + http_options: Optional[Union[HttpOptions, HttpOptionsDict]] = None + ): ... + + @property + def aio(self) -> AsyncClient: ... + + def close(self) -> None: ... + def __enter__(self) -> 'Client': ... + def __exit__(self, *args) -> None: ... + +class AsyncClient: + @property + def models(self) -> AsyncModels: ... + @property + def chats(self) -> AsyncChats: ... + @property + def files(self) -> AsyncFiles: ... + @property + def caches(self) -> AsyncCaches: ... + @property + def batches(self) -> AsyncBatches: ... + @property + def tunings(self) -> AsyncTunings: ... + @property + def file_search_stores(self) -> AsyncFileSearchStores: ... + @property + def live(self) -> AsyncLive: ... + @property + def operations(self) -> AsyncOperations: ... + + async def aclose(self) -> None: ... + async def __aenter__(self) -> 'AsyncClient': ... + async def __aexit__(self, *args) -> None: ... +``` + +[Client Initialization](./client.md) + +### Content Generation + +Generate text and multimodal content using Gemini models with support for streaming, function calling, structured output, and extensive configuration options. + +```python { .api } +def generate_content( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[GenerateContentConfig] = None +) -> GenerateContentResponse: ... + +def generate_content_stream( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[GenerateContentConfig] = None +) -> Iterator[GenerateContentResponse]: ... +``` + +[Content Generation](./content-generation.md) + +### Multi-Turn Conversations + +Create and manage chat sessions for multi-turn conversations with automatic history management. + +```python { .api } +class Chat: + def send_message( + self, + message: Union[str, Content], + config: Optional[GenerateContentConfig] = None + ) -> GenerateContentResponse: ... + + def send_message_stream( + self, + message: Union[str, Content], + config: Optional[GenerateContentConfig] = None + ) -> Iterator[GenerateContentResponse]: ... + + def get_history(self, curated: bool = False) -> list[Content]: ... +``` + +[Multi-Turn Conversations](./chats.md) + +### Embeddings + +Generate text embeddings for semantic search, clustering, and similarity comparisons. + +```python { .api } +def embed_content( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[EmbedContentConfig] = None +) -> EmbedContentResponse: ... +``` + +[Embeddings](./embeddings.md) + +### Image Generation + +Generate, edit, upscale, and segment images using Imagen models. + +```python { .api } +def generate_images( + *, + model: str, + prompt: str, + config: Optional[GenerateImagesConfig] = None +) -> GenerateImagesResponse: ... + +def edit_image( + *, + model: str, + prompt: str, + reference_images: Sequence[ReferenceImage], + config: Optional[EditImageConfig] = None +) -> EditImageResponse: ... + +def upscale_image( + *, + model: str, + image: Image, + upscale_factor: str, + config: Optional[UpscaleImageConfig] = None +) -> UpscaleImageResponse: ... +``` + +[Image Generation](./image-generation.md) + +### Video Generation + +Generate videos from prompts, images, or existing videos using Veo models. + +```python { .api } +def generate_videos( + *, + model: str, + prompt: Optional[str] = None, + image: Optional[Image] = None, + video: Optional[Video] = None, + config: Optional[GenerateVideosConfig] = None +) -> GenerateVideosOperation: ... +``` + +[Video Generation](./video-generation.md) + +### File Management + +Upload, manage, and download files for use with multimodal content generation (Gemini Developer API only). + +```python { .api } +class Files: + def upload( + self, + *, + file: Union[str, Path, IO], + config: Optional[UploadFileConfig] = None + ) -> File: ... + + def get(self, *, name: str) -> File: ... + def delete(self, *, name: str) -> None: ... + def download(self, *, name: str, path: Optional[str] = None) -> bytes: ... + def list(self, *, config: Optional[ListFilesConfig] = None) -> Union[Pager[File], Iterator[File]]: ... +``` + +[File Management](./files.md) + +### Context Caching + +Create and manage cached content to reduce costs and latency for repeated requests with shared context. + +```python { .api } +class Caches: + def create( + self, + *, + model: str, + config: CreateCachedContentConfig + ) -> CachedContent: ... + + def get(self, *, name: str) -> CachedContent: ... + def update(self, *, name: str, config: UpdateCachedContentConfig) -> CachedContent: ... + def delete(self, *, name: str) -> None: ... + def list(self, *, config: Optional[ListCachedContentsConfig] = None) -> Union[Pager[CachedContent], Iterator[CachedContent]]: ... +``` + +[Context Caching](./caching.md) + +### Batch Processing + +Submit batch prediction jobs for high-volume inference with cost savings. + +```python { .api } +class Batches: + def create( + self, + *, + model: str, + src: Union[str, list[dict]], + dest: Optional[str] = None, + config: Optional[CreateBatchJobConfig] = None + ) -> BatchJob: ... + + def create_embeddings( + self, + *, + model: str, + src: Union[str, list[dict]], + dest: Optional[str] = None, + config: Optional[CreateBatchJobConfig] = None + ) -> BatchJob: ... + + def get(self, *, name: str) -> BatchJob: ... + def cancel(self, *, name: str) -> None: ... + def delete(self, *, name: str) -> None: ... + def list(self, *, config: Optional[ListBatchJobsConfig] = None) -> Union[Pager[BatchJob], Iterator[BatchJob]]: ... +``` + +[Batch Processing](./batches.md) + +### Model Fine-Tuning + +Create and manage supervised fine-tuning jobs to customize models (Vertex AI only). + +```python { .api } +class Tunings: + def tune( + self, + *, + base_model: str, + training_dataset: TuningDataset, + config: Optional[CreateTuningJobConfig] = None + ) -> TuningJob: ... + + def get(self, *, name: str, config: Optional[GetTuningJobConfig] = None) -> TuningJob: ... + def cancel(self, *, name: str) -> None: ... + def list(self, *, config: Optional[ListTuningJobsConfig] = None) -> Union[Pager[TuningJob], Iterator[TuningJob]]: ... +``` + +[Model Fine-Tuning](./tuning.md) + +### File Search Stores + +Create and manage file search stores with document retrieval for retrieval-augmented generation. + +```python { .api } +class FileSearchStores: + def create(self, *, config: CreateFileSearchStoreConfig) -> FileSearchStore: ... + def get(self, *, name: str) -> FileSearchStore: ... + def delete(self, *, name: str) -> None: ... + def import_file( + self, + *, + store: str, + source: ImportFileSource, + config: Optional[ImportFileConfig] = None + ) -> ImportFileOperation: ... + def upload_to_file_search_store( + self, + *, + store: str, + file: Union[str, Path, IO], + config: Optional[UploadToFileSearchStoreConfig] = None + ) -> UploadToFileSearchStoreOperation: ... + def list(self, *, config: Optional[ListFileSearchStoresConfig] = None) -> Union[Pager[FileSearchStore], Iterator[FileSearchStore]]: ... +``` + +[File Search Stores](./file-search-stores.md) + +### Live API + +Real-time bidirectional streaming for interactive applications with support for audio, video, and function calling (Preview). + +```python { .api } +class AsyncLive: + async def connect( + self, + *, + model: str, + config: Optional[LiveConnectConfig] = None + ) -> AsyncIterator[AsyncSession]: ... + +class AsyncSession: + async def send_client_content( + self, + *, + turns: Optional[Union[Content, list[Content]]] = None, + turn_complete: bool = False + ) -> None: ... + + async def send_realtime_input( + self, + *, + media_chunks: Optional[Sequence[Blob]] = None + ) -> None: ... + + async def send_tool_response( + self, + *, + function_responses: Sequence[FunctionResponse] + ) -> None: ... + + async def receive(self) -> AsyncIterator[LiveServerMessage]: ... + async def close(self) -> None: ... +``` + +[Live API](./live.md) + +### Token Operations + +Count tokens and compute detailed token information for content, with support for local tokenization without API calls. + +```python { .api } +def count_tokens( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[CountTokensConfig] = None +) -> CountTokensResponse: ... + +def compute_tokens( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[ComputeTokensConfig] = None +) -> ComputeTokensResponse: ... + +class LocalTokenizer: + def __init__(self, model_name: str): ... + def count_tokens( + self, + contents: Union[str, list[Content], Content], + *, + config: Optional[CountTokensConfig] = None + ) -> CountTokensResult: ... + def compute_tokens( + self, + contents: Union[str, list[Content], Content] + ) -> ComputeTokensResult: ... +``` + +[Token Operations](./tokens.md) + +### Model Information + +Retrieve and manage model information and capabilities. + +```python { .api } +def get(self, *, model: str, config: Optional[GetModelConfig] = None) -> Model: ... +def update(self, *, model: str, config: UpdateModelConfig) -> Model: ... +def delete(self, *, model: str, config: Optional[DeleteModelConfig] = None) -> DeleteModelResponse: ... +def list(self, *, config: Optional[ListModelsConfig] = None) -> Union[Pager[Model], Iterator[Model]]: ... +``` + +[Model Information](./models.md) + +### Long-Running Operations + +Monitor and retrieve status of long-running operations like video generation and file imports. + +```python { .api } +class Operations: + def get(self, operation: Union[Operation, str]) -> Operation: ... +``` + +[Long-Running Operations](./operations.md) + +## Core Types + +### Content and Parts + +```python { .api } +class Content: + """Container for conversation content with role and parts.""" + parts: list[Part] + role: Optional[str] = None + +class Part: + """Individual content part - text, image, video, function call, etc.""" + text: Optional[str] = None + inline_data: Optional[Blob] = None + file_data: Optional[FileData] = None + function_call: Optional[FunctionCall] = None + function_response: Optional[FunctionResponse] = None + executable_code: Optional[ExecutableCode] = None + code_execution_result: Optional[CodeExecutionResult] = None + +class Blob: + """Binary data with MIME type.""" + mime_type: str + data: bytes + +class Image: + """Image data - can be URL, file path, bytes, PIL Image, or FileData.""" + # Various constructors supported + +class Video: + """Video data - can be URL, file path, bytes, or FileData.""" + # Various constructors supported +``` + +### Generation Configuration + +```python { .api } +class GenerateContentConfig: + """Configuration for content generation.""" + system_instruction: Optional[Union[str, Content]] = None + contents: Optional[Union[str, list[Content], Content]] = None + generation_config: Optional[GenerationConfig] = None + safety_settings: Optional[list[SafetySetting]] = None + tools: Optional[list[Tool]] = None + tool_config: Optional[ToolConfig] = None + cached_content: Optional[str] = None + +class GenerationConfig: + """Core generation parameters.""" + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + candidate_count: Optional[int] = None + max_output_tokens: Optional[int] = None + stop_sequences: Optional[list[str]] = None + response_mime_type: Optional[str] = None + response_schema: Optional[Schema] = None +``` + +### Response Types + +```python { .api } +class GenerateContentResponse: + """Response from content generation.""" + text: str # Convenience property + candidates: list[Candidate] + usage_metadata: Optional[GenerateContentResponseUsageMetadata] = None + prompt_feedback: Optional[GenerateContentResponsePromptFeedback] = None + +class Candidate: + """Generated candidate with content and metadata.""" + content: Content + finish_reason: Optional[FinishReason] = None + safety_ratings: Optional[list[SafetyRating]] = None + citation_metadata: Optional[CitationMetadata] = None + grounding_metadata: Optional[GroundingMetadata] = None +``` + +### Function Calling + +```python { .api } +class Tool: + """Tool containing function declarations.""" + function_declarations: Optional[list[FunctionDeclaration]] = None + google_search: Optional[GoogleSearch] = None + code_execution: Optional[ToolCodeExecution] = None + +class FunctionDeclaration: + """Function schema definition.""" + name: str + description: str + parameters: Optional[Schema] = None + +class FunctionCall: + """Function invocation from model.""" + name: str + args: dict[str, Any] + +class FunctionResponse: + """Function execution response.""" + name: str + response: dict[str, Any] +``` + +### Error Handling + +```python { .api } +class APIError(Exception): + """Base exception for API errors.""" + code: int + status: Optional[str] + message: Optional[str] + details: Any + +class ClientError(APIError): + """Client errors (4xx status codes).""" + pass + +class ServerError(APIError): + """Server errors (5xx status codes).""" + pass +``` + +## Type System + +The SDK provides comprehensive type coverage with 695+ type classes organized into categories including: + +- **Enumerations** (50+): HarmCategory, HarmBlockThreshold, FinishReason, BlockedReason, etc. +- **Content Types** (20+): Content, Part, Blob, Image, Video, FileData, etc. +- **Function Calling** (15+): Tool, FunctionDeclaration, FunctionCall, FunctionResponse, etc. +- **Retrieval & RAG** (25+): GoogleSearchRetrieval, FileSearch, VertexAISearch, VertexRagStore, etc. +- **Generation Config** (30+): GenerateContentConfig, GenerationConfig, SafetySetting, etc. +- **Response Types** (40+): GenerateContentResponse, Candidate, SafetyRating, GroundingMetadata, etc. +- **Embeddings** (10+): EmbedContentConfig, EmbedContentResponse, ContentEmbedding, etc. +- **Image Generation** (40+): GenerateImagesConfig, EditImageConfig, UpscaleImageConfig, etc. +- **Video Generation** (20+): GenerateVideosConfig, GenerateVideosResponse, GeneratedVideo, etc. +- **Model Management** (20+): Model, TunedModelInfo, Endpoint, etc. +- **Tuning** (30+): TuningJob, TuningDataset, SupervisedTuningSpec, HyperParameters, etc. +- **Batches** (20+): BatchJob, CreateBatchJobConfig, etc. +- **Caching** (15+): CachedContent, CreateCachedContentConfig, etc. +- **Files** (20+): File, FileStatus, UploadFileConfig, etc. +- **Live API** (60+): LiveConnectConfig, LiveClientMessage, LiveServerMessage, etc. + +All types are available via `from google.genai import types` and include both Pydantic models and TypedDict variants for flexible usage. diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/live.md b/.tessl/tiles/tessl/pypi-google-genai/docs/live.md new file mode 100644 index 000000000..0a8237613 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/live.md @@ -0,0 +1,473 @@ +# Live API + +Real-time bidirectional streaming for interactive applications with support for audio, video, and function calling (Preview). The Live API enables continuous, low-latency communication with the model for conversational AI, voice assistants, and real-time collaboration tools. + +## Capabilities + +### Connect to Live Session + +Establish a bidirectional streaming connection with the model. Returns an async context manager that yields a session for sending and receiving messages. + +```python { .api } +class AsyncLive: + """Asynchronous Live API (Preview).""" + + async def connect( + self, + *, + model: str, + config: Optional[LiveConnectConfig] = None + ) -> AsyncIterator[AsyncSession]: + """ + Connect to live server (async context manager). + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash-exp'). + config (LiveConnectConfig, optional): Connection configuration including: + - system_instruction: System instruction for the session + - generation_config: Generation parameters + - tools: Function declarations for function calling + - speech_config: Speech synthesis configuration + + Yields: + AsyncSession: Active session for bidirectional communication. + + Raises: + ClientError: For client errors + ServerError: For server errors + + Usage: + async with client.aio.live.connect(model='gemini-2.0-flash-exp') as session: + # Send and receive messages + pass + """ + ... +``` + +**Usage Example - Basic Live Session:** + +```python +import asyncio +from google.genai import Client + +async def live_session(): + client = Client(api_key='YOUR_API_KEY') + + async with client.aio.live.connect( + model='gemini-2.0-flash-exp' + ) as session: + # Send message + await session.send_client_content( + turns=[Content(parts=[Part(text='Hello, how are you?')])], + turn_complete=True + ) + + # Receive responses + async for message in session.receive(): + if message.server_content: + if message.server_content.model_turn: + for part in message.server_content.model_turn.parts: + if part.text: + print(part.text, end='', flush=True) + if message.server_content.turn_complete: + print() # New line after turn completes + break + +asyncio.run(live_session()) +``` + +### Send Client Content + +Send conversational content (text, images, etc.) to the model during a live session. + +```python { .api } +class AsyncSession: + """Live session for bidirectional streaming (Preview).""" + + async def send_client_content( + self, + *, + turns: Optional[Union[Content, list[Content]]] = None, + turn_complete: bool = False + ) -> None: + """ + Send client content to the model. + + Parameters: + turns (Union[Content, list[Content]], optional): Content to send. Can be + text, images, or other supported content types. + turn_complete (bool): Whether this completes the current turn. If True, + model will begin generating response. Default: False. + + Raises: + RuntimeError: If session is closed or invalid state + """ + ... +``` + +### Send Realtime Input + +Send realtime media chunks (audio, video) for streaming input. + +```python { .api } +class AsyncSession: + """Live session for bidirectional streaming (Preview).""" + + async def send_realtime_input( + self, + *, + media_chunks: Optional[Sequence[Blob]] = None + ) -> None: + """ + Send realtime media input (audio/video chunks). + + Parameters: + media_chunks (Sequence[Blob], optional): Media chunks to send. Each Blob + contains binary data with MIME type (e.g., 'audio/pcm' for raw audio). + + Raises: + RuntimeError: If session is closed or invalid state + """ + ... +``` + +**Usage Example - Audio Streaming:** + +```python +import asyncio +from google.genai import Client +from google.genai.types import Blob + +async def stream_audio(): + client = Client(api_key='YOUR_API_KEY') + + async with client.aio.live.connect( + model='gemini-2.0-flash-exp' + ) as session: + # Stream audio chunks + with open('audio.pcm', 'rb') as f: + while chunk := f.read(4096): + await session.send_realtime_input( + media_chunks=[Blob( + mime_type='audio/pcm', + data=chunk + )] + ) + + # Receive responses + async for message in session.receive(): + # Process responses + pass + +asyncio.run(stream_audio()) +``` + +### Send Tool Response + +Send function execution results back to the model after it requests a function call. + +```python { .api } +class AsyncSession: + """Live session for bidirectional streaming (Preview).""" + + async def send_tool_response( + self, + *, + function_responses: Sequence[FunctionResponse] + ) -> None: + """ + Send tool/function execution responses. + + Parameters: + function_responses (Sequence[FunctionResponse]): Results from executing + functions that the model requested. + + Raises: + RuntimeError: If session is closed or invalid state + """ + ... +``` + +### Receive Messages + +Receive streaming messages from the model including content, function calls, and metadata. + +```python { .api } +class AsyncSession: + """Live session for bidirectional streaming (Preview).""" + + async def receive(self) -> AsyncIterator[LiveServerMessage]: + """ + Receive messages from the model as an async iterator. + + Yields: + LiveServerMessage: Server messages including: + - setup_complete: Connection setup confirmation + - server_content: Generated content from model + - tool_call: Function call requests from model + - tool_call_cancellation: Cancelled function calls + + Raises: + RuntimeError: If session is closed + """ + ... +``` + +### Close Session + +Explicitly close the live session. + +```python { .api } +class AsyncSession: + """Live session for bidirectional streaming (Preview).""" + + async def close(self) -> None: + """ + Close the live session. + + Note: Sessions are automatically closed when exiting the context manager. + """ + ... +``` + +**Usage Example - Function Calling:** + +```python +import asyncio +from google.genai import Client +from google.genai.types import ( + LiveConnectConfig, + Tool, + FunctionDeclaration, + Schema, + Type, + Content, + Part, + FunctionResponse +) + +async def live_with_functions(): + client = Client(api_key='YOUR_API_KEY') + + # Define function + weather_func = FunctionDeclaration( + name='get_weather', + description='Get weather for a location', + parameters=Schema( + type=Type.OBJECT, + properties={ + 'location': Schema(type=Type.STRING) + } + ) + ) + + config = LiveConnectConfig( + tools=[Tool(function_declarations=[weather_func])] + ) + + async with client.aio.live.connect( + model='gemini-2.0-flash-exp', + config=config + ) as session: + # Ask for weather + await session.send_client_content( + turns=[Content(parts=[Part(text='What is the weather in Tokyo?')])], + turn_complete=True + ) + + # Handle responses + async for message in session.receive(): + if message.tool_call: + # Execute function + if message.tool_call.function_calls: + for fc in message.tool_call.function_calls: + if fc.name == 'get_weather': + # Execute and return result + await session.send_tool_response( + function_responses=[FunctionResponse( + name='get_weather', + response={'temperature': 22, 'condition': 'sunny'}, + id=fc.id + )] + ) + + if message.server_content: + if message.server_content.model_turn: + for part in message.server_content.model_turn.parts: + if part.text: + print(part.text) + if message.server_content.turn_complete: + break + +asyncio.run(live_with_functions()) +``` + +## Types + +```python { .api } +from typing import Optional, Union, Sequence, AsyncIterator, Any +from enum import Enum + +# Configuration types +class LiveConnectConfig: + """ + Configuration for live connection. + + Attributes: + system_instruction (Union[str, Content], optional): System instruction. + generation_config (GenerationConfig, optional): Generation parameters. + tools (list[Tool], optional): Function declarations. + speech_config (SpeechConfig, optional): Speech synthesis configuration. + response_modalities (list[str], optional): Desired response modalities ('TEXT', 'AUDIO'). + """ + system_instruction: Optional[Union[str, Content]] = None + generation_config: Optional[GenerationConfig] = None + tools: Optional[list[Tool]] = None + speech_config: Optional[SpeechConfig] = None + response_modalities: Optional[list[str]] = None + +class SpeechConfig: + """ + Speech synthesis configuration. + + Attributes: + voice_config (VoiceConfig, optional): Voice settings. + """ + voice_config: Optional[VoiceConfig] = None + +class VoiceConfig: + """ + Voice configuration. + + Attributes: + prebuilt_voice_config (PrebuiltVoiceConfig, optional): Prebuilt voice. + """ + prebuilt_voice_config: Optional[PrebuiltVoiceConfig] = None + +class PrebuiltVoiceConfig: + """ + Prebuilt voice settings. + + Attributes: + voice_name (str, optional): Voice name (e.g., 'Aoede', 'Charon'). + """ + voice_name: Optional[str] = None + +# Message types +class LiveServerMessage: + """ + Message from server in live session. + + Attributes: + setup_complete (LiveServerSetupComplete, optional): Setup confirmation. + server_content (LiveServerContent, optional): Generated content. + tool_call (LiveServerToolCall, optional): Function call request. + tool_call_cancellation (LiveServerToolCallCancellation, optional): Cancelled call. + """ + setup_complete: Optional[LiveServerSetupComplete] = None + server_content: Optional[LiveServerContent] = None + tool_call: Optional[LiveServerToolCall] = None + tool_call_cancellation: Optional[LiveServerToolCallCancellation] = None + +class LiveServerSetupComplete: + """Setup complete confirmation.""" + pass + +class LiveServerContent: + """ + Server content message. + + Attributes: + model_turn (Content, optional): Model's turn content. + turn_complete (bool, optional): Whether turn is complete. + interrupted (bool, optional): Whether turn was interrupted. + grounding_metadata (GroundingMetadata, optional): Grounding information. + """ + model_turn: Optional[Content] = None + turn_complete: Optional[bool] = None + interrupted: Optional[bool] = None + grounding_metadata: Optional[GroundingMetadata] = None + +class LiveServerToolCall: + """ + Tool call request from server. + + Attributes: + function_calls (list[FunctionCall], optional): Requested function calls. + """ + function_calls: Optional[list[FunctionCall]] = None + +class LiveServerToolCallCancellation: + """ + Tool call cancellation. + + Attributes: + ids (list[str], optional): IDs of cancelled calls. + """ + ids: Optional[list[str]] = None + +# Core types (shared with other capabilities) +class Content: + """Content container.""" + parts: list[Part] + role: Optional[str] = None + +class Part: + """Content part.""" + text: Optional[str] = None + inline_data: Optional[Blob] = None + function_call: Optional[FunctionCall] = None + function_response: Optional[FunctionResponse] = None + +class Blob: + """Binary data.""" + mime_type: str + data: bytes + +class FunctionCall: + """Function call from model.""" + name: str + args: dict[str, Any] + id: Optional[str] = None + +class FunctionResponse: + """Function execution result.""" + name: str + response: dict[str, Any] + id: Optional[str] = None + +class Tool: + """Tool with function declarations.""" + function_declarations: Optional[list[FunctionDeclaration]] = None + +class FunctionDeclaration: + """Function definition.""" + name: str + description: str + parameters: Optional[Schema] = None + +class Schema: + """JSON schema.""" + type: Type + properties: Optional[dict[str, Schema]] = None + +class Type(Enum): + """JSON schema types.""" + OBJECT = 'OBJECT' + STRING = 'STRING' + NUMBER = 'NUMBER' + INTEGER = 'INTEGER' + BOOLEAN = 'BOOLEAN' + ARRAY = 'ARRAY' + +class GenerationConfig: + """Generation configuration.""" + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + max_output_tokens: Optional[int] = None + +class GroundingMetadata: + """Grounding metadata.""" + pass +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/models.md b/.tessl/tiles/tessl/pypi-google-genai/docs/models.md new file mode 100644 index 000000000..3a5a934ca --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/models.md @@ -0,0 +1,355 @@ +# Model Information and Management + +Retrieve and manage model information and capabilities. Access model metadata, update model settings, delete tuned models, and list available models. + +## Capabilities + +### Get Model + +Retrieve information about a specific model including capabilities and configuration. + +```python { .api } +def get( + self, + *, + model: str, + config: Optional[GetModelConfig] = None +) -> Model: + """ + Get model information. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). + For tuned models, use full resource name. + config (GetModelConfig, optional): Get configuration. + + Returns: + Model: Model information including: + - Supported generation methods + - Input/output token limits + - Supported features (function calling, multimodal, etc.) + - Model version + + Raises: + ClientError: For client errors including 404 if model not found + ServerError: For server errors (5xx status codes) + """ + ... + +async def get( + self, + *, + model: str, + config: Optional[GetModelConfig] = None +) -> Model: + """Async version of get.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Get model info +model = client.models.get(model='gemini-2.0-flash') + +print(f"Model: {model.name}") +print(f"Display name: {model.display_name}") +print(f"Description: {model.description}") +print(f"Input token limit: {model.input_token_limit}") +print(f"Output token limit: {model.output_token_limit}") +print(f"Supported methods: {model.supported_generation_methods}") + +# Check capabilities +if 'generateContent' in model.supported_generation_methods: + print("Supports content generation") +if 'embedContent' in model.supported_generation_methods: + print("Supports embeddings") +``` + +### Update Model + +Update mutable model metadata such as display name and description (for tuned models). + +```python { .api } +def update( + self, + *, + model: str, + config: UpdateModelConfig +) -> Model: + """ + Update model metadata (tuned models only). + + Parameters: + model (str): Model resource name. + config (UpdateModelConfig): Update configuration including: + - display_name: New display name + - description: New description + + Returns: + Model: Updated model information. + + Raises: + ClientError: For client errors + ServerError: For server errors + """ + ... + +async def update( + self, + *, + model: str, + config: UpdateModelConfig +) -> Model: + """Async version of update.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import UpdateModelConfig + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +config = UpdateModelConfig( + display_name='My Custom Model v2', + description='Updated description' +) + +updated_model = client.models.update( + model='projects/.../locations/.../models/my-model', + config=config +) + +print(f"Updated: {updated_model.display_name}") +``` + +### Delete Model + +Delete a tuned model. + +```python { .api } +def delete( + self, + *, + model: str, + config: Optional[DeleteModelConfig] = None +) -> DeleteModelResponse: + """ + Delete a tuned model. + + Parameters: + model (str): Model resource name to delete. + config (DeleteModelConfig, optional): Delete configuration. + + Returns: + DeleteModelResponse: Deletion confirmation. + + Raises: + ClientError: For client errors including 404 if model not found + ServerError: For server errors + """ + ... + +async def delete( + self, + *, + model: str, + config: Optional[DeleteModelConfig] = None +) -> DeleteModelResponse: + """Async version of delete.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +response = client.models.delete( + model='projects/.../locations/.../models/my-old-model' +) + +print("Model deleted") +``` + +### List Models + +List all available models with optional pagination and filtering. + +```python { .api } +def list( + self, + *, + config: Optional[ListModelsConfig] = None +) -> Union[Pager[Model], Iterator[Model]]: + """ + List available models. + + Parameters: + config (ListModelsConfig, optional): List configuration including: + - page_size: Number of models per page + - page_token: Token for pagination + - filter: Filter expression + + Returns: + Union[Pager[Model], Iterator[Model]]: Paginated model list. + + Raises: + ClientError: For client errors + ServerError: For server errors + """ + ... + +async def list( + self, + *, + config: Optional[ListModelsConfig] = None +) -> Union[AsyncPager[Model], AsyncIterator[Model]]: + """Async version of list.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# List all available models +print("Available models:") +for model in client.models.list(): + print(f"- {model.name}: {model.display_name}") + print(f" Methods: {', '.join(model.supported_generation_methods)}") + +# List with pagination +from google.genai.types import ListModelsConfig + +config = ListModelsConfig(page_size=10) +pager = client.models.list(config=config) + +print(f"\nFirst page ({len(pager.page)} models):") +for model in pager.page: + print(f"- {model.name}") +``` + +## Types + +```python { .api } +from typing import Optional, List, Iterator, AsyncIterator, Union + +# Configuration types +class GetModelConfig: + """Configuration for getting model.""" + pass + +class UpdateModelConfig: + """ + Configuration for updating model. + + Attributes: + display_name (str, optional): New display name. + description (str, optional): New description. + """ + display_name: Optional[str] = None + description: Optional[str] = None + +class DeleteModelConfig: + """Configuration for deleting model.""" + pass + +class ListModelsConfig: + """ + Configuration for listing models. + + Attributes: + page_size (int, optional): Number of models per page. + page_token (str, optional): Token for pagination. + filter (str, optional): Filter expression. + """ + page_size: Optional[int] = None + page_token: Optional[str] = None + filter: Optional[str] = None + +# Response types +class Model: + """ + Model information and capabilities. + + Attributes: + name (str): Model resource name (e.g., 'models/gemini-2.0-flash'). + base_model_id (str, optional): Base model identifier. + version (str, optional): Model version. + display_name (str): Human-readable display name. + description (str): Model description. + input_token_limit (int): Maximum input tokens. + output_token_limit (int): Maximum output tokens. + supported_generation_methods (list[str]): Supported methods: + - 'generateContent': Text/multimodal generation + - 'embedContent': Embeddings + - 'generateImages': Image generation + - 'generateVideos': Video generation + temperature (float, optional): Default temperature. + top_p (float, optional): Default top_p. + top_k (int, optional): Default top_k. + max_temperature (float, optional): Maximum allowed temperature. + tuned_model_info (TunedModelInfo, optional): Info for tuned models. + """ + name: str + base_model_id: Optional[str] = None + version: Optional[str] = None + display_name: str + description: str + input_token_limit: int + output_token_limit: int + supported_generation_methods: list[str] + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + max_temperature: Optional[float] = None + tuned_model_info: Optional[TunedModelInfo] = None + +class TunedModelInfo: + """ + Information for tuned models. + + Attributes: + tuning_job (str): Tuning job that created this model. + base_model (str): Base model used for tuning. + tuning_dataset (str, optional): Training dataset. + """ + tuning_job: str + base_model: str + tuning_dataset: Optional[str] = None + +class DeleteModelResponse: + """ + Response from deleting a model. + + Attributes: + deleted (bool): Whether deletion succeeded. + """ + deleted: bool + +# Pager types +class Pager[T]: + """Synchronous pager.""" + page: list[T] + def next_page(self) -> None: ... + def __iter__(self) -> Iterator[T]: ... + +class AsyncPager[T]: + """Asynchronous pager.""" + page: list[T] + async def next_page(self) -> None: ... + async def __aiter__(self) -> AsyncIterator[T]: ... +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/operations.md b/.tessl/tiles/tessl/pypi-google-genai/docs/operations.md new file mode 100644 index 000000000..0ce73223b --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/operations.md @@ -0,0 +1,290 @@ +# Long-Running Operations + +Monitor and retrieve status of long-running operations like video generation, file imports, and other asynchronous tasks. Operations provide a way to track progress and retrieve results for tasks that take significant time to complete. + +## Capabilities + +### Get Operation + +Retrieve the current status and result of a long-running operation. + +```python { .api } +class Operations: + """Synchronous long-running operations API.""" + + def get(self, operation: Union[Operation, str]) -> Operation: + """ + Get operation status and result. + + Parameters: + operation (Union[Operation, str]): Operation to check. Can be: + - Operation: Operation object to refresh + - str: Operation name/resource name + + Returns: + Operation: Updated operation with current status. Check operation.done + to see if complete. If done, check operation.error for errors or + operation.response for results. + + Raises: + ClientError: For client errors including 404 if operation not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncOperations: + """Asynchronous long-running operations API.""" + + async def get(self, operation: Union[Operation, str]) -> Operation: + """Async version of get.""" + ... +``` + +**Usage Example - Video Generation:** + +```python +import time +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Start video generation +operation = client.models.generate_videos( + model='veo-2.0-generate-001', + prompt='A cat playing with a ball' +) + +print(f"Operation started: {operation.name}") +print(f"Done: {operation.done}") + +# Poll for completion +while not operation.done: + print("Waiting for completion...") + time.sleep(10) + + # Refresh operation status + operation = client.operations.get(operation) + print(f"Done: {operation.done}") + +# Check result +if operation.error: + print(f"Operation failed: {operation.error.message}") +else: + print("Operation succeeded!") + response = operation.response + + # Access video generation response + for i, video in enumerate(response.generated_videos): + with open(f'video_{i}.mp4', 'wb') as f: + f.write(video.video.data) + print(f"Saved video_{i}.mp4") +``` + +**Usage Example - File Import:** + +```python +from google.genai import Client +from google.genai.types import ImportFileSource + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Start file import +source = ImportFileSource(gcs_uri='gs://my-bucket/document.pdf') +operation = client.file_search_stores.import_file( + store='fileSearchStores/abc123', + source=source +) + +print(f"Import operation: {operation.name}") + +# Poll until done +while not operation.done: + import time + time.sleep(5) + operation = client.operations.get(operation) + +if operation.error: + print(f"Import failed: {operation.error.message}") +else: + print("Import completed successfully") +``` + +**Usage Example - Async Polling:** + +```python +import asyncio +from google.genai import Client + +async def wait_for_operation(): + client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + + # Start operation + operation = await client.aio.models.generate_videos( + model='veo-2.0-generate-001', + prompt='A sunset over mountains' + ) + + print(f"Operation: {operation.name}") + + # Poll asynchronously + while not operation.done: + print("Checking status...") + await asyncio.sleep(10) + operation = await client.aio.operations.get(operation) + + if operation.error: + print(f"Failed: {operation.error.message}") + else: + print(f"Success! Generated {len(operation.response.generated_videos)} video(s)") + +asyncio.run(wait_for_operation()) +``` + +**Usage Example - Operation Metadata:** + +```python +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Get operation +operation = client.operations.get('operations/abc123') + +print(f"Operation: {operation.name}") +print(f"Done: {operation.done}") + +# Check metadata for progress +if operation.metadata: + print(f"Metadata: {operation.metadata}") + # Metadata structure varies by operation type + # May include progress percentage, timestamps, etc. + +# Check if operation completed +if operation.done: + if operation.error: + print(f"Error code: {operation.error.code}") + print(f"Error message: {operation.error.message}") + if operation.error.details: + print(f"Details: {operation.error.details}") + else: + print("Operation completed successfully") + # Access operation.response for results +``` + +## Types + +```python { .api } +from typing import Optional, Union, Any, Dict + +# Operation base class +class Operation: + """ + Long-running operation. + + This is an abstract base class. Specific operation types inherit from it + (e.g., GenerateVideosOperation, ImportFileOperation). + + Attributes: + name (str): Operation resource name for polling. + Format varies by operation type (e.g., 'operations/{id}'). + done (bool): Whether operation has completed (successfully or with error). + error (OperationError, optional): Error if operation failed. Only set if + done=True and operation failed. + response (Any, optional): Operation result if successful. Only set if + done=True and operation succeeded. Type depends on operation: + - GenerateVideosOperation.response: GenerateVideosResponse + - ImportFileOperation.response: ImportFileResponse + - etc. + metadata (Dict[str, Any], optional): Operation metadata including: + - Progress information + - Timestamps + - Operation-specific data + Structure varies by operation type. + """ + name: str + done: bool + error: Optional[OperationError] = None + response: Optional[Any] = None + metadata: Optional[Dict[str, Any]] = None + +class OperationError: + """ + Error information for failed operation. + + Attributes: + code (int): Error code (HTTP status code or gRPC code). + message (str): Human-readable error message. + details (list, optional): Additional error details. Structure varies by + error type and may include: + - Error metadata + - Retry information + - Debugging information + """ + code: int + message: str + details: Optional[list] = None + +# Specific operation types (inherit from Operation) +class GenerateVideosOperation(Operation): + """ + Video generation operation. + + Attributes: + response (GenerateVideosResponse, optional): Video generation response when done. + """ + response: Optional[GenerateVideosResponse] = None + +class ImportFileOperation(Operation): + """ + File import operation. + + Attributes: + response (Dict, optional): Import response when done. + """ + response: Optional[Dict] = None + +class UploadToFileSearchStoreOperation(Operation): + """ + File upload operation for file search stores. + + Attributes: + response (Dict, optional): Upload response when done. + """ + response: Optional[Dict] = None + +# Response types (referenced by operations) +class GenerateVideosResponse: + """ + Video generation response (from GenerateVideosOperation). + + Attributes: + generated_videos (list[GeneratedVideo]): Generated videos. + rai_media_filtered_count (int, optional): Safety-filtered count. + rai_media_filtered_reasons (list[str], optional): Filter reasons. + """ + generated_videos: list[GeneratedVideo] + rai_media_filtered_count: Optional[int] = None + rai_media_filtered_reasons: Optional[list[str]] = None + +class GeneratedVideo: + """ + Generated video with metadata. + + Attributes: + video (Video): Video object with data. + generation_seed (int, optional): Generation seed. + """ + video: Video + generation_seed: Optional[int] = None + +class Video: + """ + Video data. + + Attributes: + data (bytes): Video binary data. + mime_type (str): MIME type (e.g., 'video/mp4'). + """ + data: bytes + mime_type: str +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/tokens.md b/.tessl/tiles/tessl/pypi-google-genai/docs/tokens.md new file mode 100644 index 000000000..656b1631c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/tokens.md @@ -0,0 +1,328 @@ +# Token Operations and Local Tokenizer + +Count tokens and compute detailed token information for content, with support for local tokenization without API calls. Token counting helps estimate costs, manage context limits, and optimize prompts. + +## Capabilities + +### Count Tokens + +Count the number of tokens in content using the API. + +```python { .api } +def count_tokens( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[CountTokensConfig] = None +) -> CountTokensResponse: + """ + Count tokens in content via API. + + Parameters: + model (str): Model identifier (e.g., 'gemini-2.0-flash'). + contents (Union[str, list[Content], Content]): Content to count tokens for. + config (CountTokensConfig, optional): Configuration including: + - generation_config: Generation config to count with + - system_instruction: System instruction to include + - tools: Tools to include + + Returns: + CountTokensResponse: Token count including prompt tokens. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def count_tokens( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[CountTokensConfig] = None +) -> CountTokensResponse: + """Async version of count_tokens.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +# Count tokens in text +response = client.models.count_tokens( + model='gemini-2.0-flash', + contents='How many tokens is this sentence?' +) + +print(f"Total tokens: {response.total_tokens}") + +# Count tokens with context +from google.genai.types import CountTokensConfig, Content, Part + +config = CountTokensConfig( + system_instruction='You are a helpful assistant.' +) + +response = client.models.count_tokens( + model='gemini-2.0-flash', + contents=[ + Content(role='user', parts=[Part(text='Hello')]), + Content(role='model', parts=[Part(text='Hi there!')]), + Content(role='user', parts=[Part(text='How are you?')]) + ], + config=config +) + +print(f"Total tokens including context: {response.total_tokens}") +``` + +### Compute Tokens + +Compute detailed token information including token IDs via API. + +```python { .api } +def compute_tokens( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[ComputeTokensConfig] = None +) -> ComputeTokensResponse: + """ + Compute detailed token information via API. + + Parameters: + model (str): Model identifier. + contents (Union[str, list[Content], Content]): Content to tokenize. + config (ComputeTokensConfig, optional): Configuration. + + Returns: + ComputeTokensResponse: Detailed token information including token IDs. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def compute_tokens( + *, + model: str, + contents: Union[str, list[Content], Content], + config: Optional[ComputeTokensConfig] = None +) -> ComputeTokensResponse: + """Async version of compute_tokens.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(api_key='YOUR_API_KEY') + +response = client.models.compute_tokens( + model='gemini-2.0-flash', + contents='Hello world' +) + +print(f"Total tokens: {response.total_tokens}") + +for token_info in response.tokens_info: + for token in token_info.tokens: + print(f"Token: '{token.token}' (ID: {token.token_id})") +``` + +### Local Tokenizer + +Perform token counting locally without API calls (Experimental). The local tokenizer downloads model tokenizer data once and performs tokenization locally, useful for offline scenarios or reducing API calls. + +```python { .api } +class LocalTokenizer: + """Local tokenizer for text-only token counting (Experimental).""" + + def __init__(self, model_name: str): + """ + Initialize local tokenizer for a model. + + Parameters: + model_name (str): Model name (e.g., 'gemini-2.0-flash'). + Downloads tokenizer data on first use. + + Raises: + ValueError: If model doesn't support local tokenization + """ + ... + + def count_tokens( + self, + contents: Union[str, list[Content], Content], + *, + config: Optional[CountTokensConfig] = None + ) -> CountTokensResult: + """ + Count tokens locally (text-only). + + Parameters: + contents (Union[str, list[Content], Content]): Content to count. + Only text parts are counted; images/audio are ignored. + config (CountTokensConfig, optional): Configuration. + + Returns: + CountTokensResult: Token count. Note: May differ slightly from API + count due to local approximation. + """ + ... + + def compute_tokens( + self, + contents: Union[str, list[Content], Content] + ) -> ComputeTokensResult: + """ + Compute detailed token information locally. + + Parameters: + contents (Union[str, list[Content], Content]): Content to tokenize. + + Returns: + ComputeTokensResult: Detailed token information with IDs. + """ + ... +``` + +**Usage Example:** + +```python +from google.genai.local_tokenizer import LocalTokenizer + +# Initialize tokenizer (downloads data on first use) +tokenizer = LocalTokenizer('gemini-2.0-flash') + +# Count tokens locally +result = tokenizer.count_tokens('How many tokens is this?') +print(f"Total tokens (local): {result.total_tokens}") + +# Compute detailed tokens +result = tokenizer.compute_tokens('Hello world') +for token_info in result.tokens_info: + for token in token_info.tokens: + print(f"Token: '{token.token}' (ID: {token.token_id})") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List + +# Configuration types +class CountTokensConfig: + """ + Configuration for token counting. + + Attributes: + generation_config (GenerationConfig, optional): Generation config to include. + system_instruction (Union[str, Content], optional): System instruction to include. + tools (list[Tool], optional): Tools to include in count. + """ + generation_config: Optional[GenerationConfig] = None + system_instruction: Optional[Union[str, Content]] = None + tools: Optional[list[Tool]] = None + +class ComputeTokensConfig: + """Configuration for computing tokens.""" + pass + +# Response types +class CountTokensResponse: + """ + Response from count_tokens API. + + Attributes: + total_tokens (int): Total number of tokens. + total_billable_characters (int, optional): Billable characters count (for embeddings). + """ + total_tokens: int + total_billable_characters: Optional[int] = None + +class ComputeTokensResponse: + """ + Response from compute_tokens API. + + Attributes: + tokens_info (list[TokensInfo]): Detailed token information per content part. + total_tokens (int): Total number of tokens. + """ + tokens_info: list[TokensInfo] + total_tokens: int + +class TokensInfo: + """ + Token information for content. + + Attributes: + tokens (list[Token]): Individual tokens. + role (str, optional): Content role. + """ + tokens: list[Token] + role: Optional[str] = None + +class Token: + """ + Individual token. + + Attributes: + token (str): Token string. + token_id (int): Token ID in model vocabulary. + """ + token: str + token_id: int + +# Local tokenizer result types +class CountTokensResult: + """ + Result from local token counting. + + Attributes: + total_tokens (int): Total number of tokens. + """ + total_tokens: int + +class ComputeTokensResult: + """ + Result from local token computation. + + Attributes: + tokens_info (list[TokensInfo]): Detailed token information. + total_tokens (int): Total number of tokens. + """ + tokens_info: list[TokensInfo] + total_tokens: int + +# Core types (shared) +class Content: + """Content container.""" + parts: list[Part] + role: Optional[str] = None + +class Part: + """Content part.""" + text: Optional[str] = None + inline_data: Optional[Blob] = None + +class Blob: + """Binary data.""" + mime_type: str + data: bytes + +class GenerationConfig: + """Generation configuration.""" + pass + +class Tool: + """Tool definition.""" + pass +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/tuning.md b/.tessl/tiles/tessl/pypi-google-genai/docs/tuning.md new file mode 100644 index 000000000..c8c53f49a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/tuning.md @@ -0,0 +1,405 @@ +# Model Fine-Tuning + +Create and manage supervised fine-tuning jobs to customize models on your data (Vertex AI only). Fine-tuning allows you to adapt foundation models to your specific use case by training on your own datasets, improving model performance for domain-specific tasks. + +## Capabilities + +### Tune Model + +Create a supervised fine-tuning job to customize a base model. + +```python { .api } +class Tunings: + """Synchronous tuning jobs API (Vertex AI only).""" + + def tune( + self, + *, + base_model: str, + training_dataset: TuningDataset, + config: Optional[CreateTuningJobConfig] = None + ) -> TuningJob: + """ + Create a supervised fine-tuning job. + + Parameters: + base_model (str): Base model to fine-tune (e.g., 'gemini-1.5-flash-002'). + training_dataset (TuningDataset): Training dataset specification including + GCS URI or inline examples. + config (CreateTuningJobConfig, optional): Tuning configuration including: + - tuned_model_display_name: Display name for tuned model + - epoch_count: Number of training epochs + - learning_rate: Learning rate + - adapter_size: Adapter size for efficient tuning + - validation_dataset: Optional validation dataset + + Returns: + TuningJob: Created tuning job with name and initial status. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncTunings: + """Asynchronous tuning jobs API (Vertex AI only).""" + + async def tune( + self, + *, + base_model: str, + training_dataset: TuningDataset, + config: Optional[CreateTuningJobConfig] = None + ) -> TuningJob: + """Async version of tune.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client +from google.genai.types import ( + TuningDataset, + CreateTuningJobConfig, + HyperParameters, + AdapterSize +) + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Define training dataset +training_dataset = TuningDataset( + gcs_uri='gs://my-bucket/training_data.jsonl' +) + +# Configure tuning +config = CreateTuningJobConfig( + tuned_model_display_name='my-custom-model', + epoch_count=5, + learning_rate_multiplier=1.0, + adapter_size=AdapterSize.ADAPTER_SIZE_SIXTEEN, + validation_dataset=TuningDataset( + gcs_uri='gs://my-bucket/validation_data.jsonl' + ) +) + +# Start tuning +job = client.tunings.tune( + base_model='gemini-1.5-flash-002', + training_dataset=training_dataset, + config=config +) + +print(f"Tuning job created: {job.name}") +print(f"State: {job.state}") + +# Poll for completion +import time +while job.state in ['JOB_STATE_PENDING', 'JOB_STATE_RUNNING']: + time.sleep(60) + job = client.tunings.get(name=job.name) + print(f"State: {job.state}") + +if job.state == 'JOB_STATE_SUCCEEDED': + print(f"Tuned model: {job.tuned_model.model}") + + # Use tuned model + response = client.models.generate_content( + model=job.tuned_model.model, + contents='Test the tuned model' + ) + print(response.text) +``` + +### Get Tuning Job + +Retrieve information about a tuning job including status and progress. + +```python { .api } +class Tunings: + """Synchronous tuning jobs API (Vertex AI only).""" + + def get( + self, + *, + name: str, + config: Optional[GetTuningJobConfig] = None + ) -> TuningJob: + """ + Get tuning job information. + + Parameters: + name (str): Job name in format 'projects/*/locations/*/tuningJobs/*'. + config (GetTuningJobConfig, optional): Get configuration. + + Returns: + TuningJob: Job information including state, progress, and tuned model. + + Raises: + ClientError: For client errors including 404 if job not found + ServerError: For server errors (5xx status codes) + """ + ... + +class AsyncTunings: + """Asynchronous tuning jobs API (Vertex AI only).""" + + async def get( + self, + *, + name: str, + config: Optional[GetTuningJobConfig] = None + ) -> TuningJob: + """Async version of get.""" + ... +``` + +### Cancel Tuning Job + +Cancel a running tuning job. + +```python { .api } +class Tunings: + """Synchronous tuning jobs API (Vertex AI only).""" + + def cancel(self, *, name: str) -> None: + """ + Cancel a tuning job. + + Parameters: + name (str): Job name in format 'projects/*/locations/*/tuningJobs/*'. + + Raises: + ClientError: For client errors + ServerError: For server errors + """ + ... + +class AsyncTunings: + """Asynchronous tuning jobs API (Vertex AI only).""" + + async def cancel(self, *, name: str) -> None: + """Async version of cancel.""" + ... +``` + +### List Tuning Jobs + +List all tuning jobs with optional filtering and pagination. + +```python { .api } +class Tunings: + """Synchronous tuning jobs API (Vertex AI only).""" + + def list( + self, + *, + config: Optional[ListTuningJobsConfig] = None + ) -> Union[Pager[TuningJob], Iterator[TuningJob]]: + """ + List tuning jobs. + + Parameters: + config (ListTuningJobsConfig, optional): List configuration including: + - page_size: Number of jobs per page + - page_token: Token for pagination + - filter: Filter expression + + Returns: + Union[Pager[TuningJob], Iterator[TuningJob]]: Paginated job list. + + Raises: + ClientError: For client errors + ServerError: For server errors + """ + ... + +class AsyncTunings: + """Asynchronous tuning jobs API (Vertex AI only).""" + + async def list( + self, + *, + config: Optional[ListTuningJobsConfig] = None + ) -> Union[AsyncPager[TuningJob], AsyncIterator[TuningJob]]: + """Async version of list.""" + ... +``` + +**Usage Example:** + +```python +from google.genai import Client + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# List all tuning jobs +for job in client.tunings.list(): + print(f"{job.name}: {job.state}") + if job.tuned_model: + print(f" Tuned model: {job.tuned_model.model}") +``` + +## Types + +```python { .api } +from typing import Optional, Union, List, Iterator, AsyncIterator, Dict +from datetime import datetime +from enum import Enum + +# Configuration types +class CreateTuningJobConfig: + """ + Configuration for creating tuning jobs. + + Attributes: + tuned_model_display_name (str, optional): Display name for the tuned model. + epoch_count (int, optional): Number of training epochs (1-100). Default: 5. + learning_rate_multiplier (float, optional): Learning rate multiplier (0.001-10.0). + Default: 1.0. + adapter_size (AdapterSize, optional): Adapter size for efficient tuning. + Default: ADAPTER_SIZE_ONE. + validation_dataset (TuningDataset, optional): Validation dataset. + labels (dict[str, str], optional): Job labels. + """ + tuned_model_display_name: Optional[str] = None + epoch_count: Optional[int] = None + learning_rate_multiplier: Optional[float] = None + adapter_size: Optional[AdapterSize] = None + validation_dataset: Optional[TuningDataset] = None + labels: Optional[dict[str, str]] = None + +class GetTuningJobConfig: + """Configuration for getting tuning job.""" + pass + +class ListTuningJobsConfig: + """ + Configuration for listing tuning jobs. + + Attributes: + page_size (int, optional): Number of jobs per page. + page_token (str, optional): Token for pagination. + filter (str, optional): Filter expression. + """ + page_size: Optional[int] = None + page_token: Optional[str] = None + filter: Optional[str] = None + +class TuningDataset: + """ + Training or validation dataset specification. + + Attributes: + gcs_uri (str, optional): GCS URI to JSONL file (e.g., 'gs://bucket/data.jsonl'). + Each line should be a JSON object with 'contents' field. + """ + gcs_uri: Optional[str] = None + +# Response types +class TuningJob: + """ + Tuning job information. + + Attributes: + name (str): Job resource name. + base_model (str): Base model being tuned. + tuned_model (TunedModel, optional): Information about the tuned model. + state (JobState): Current job state. + create_time (datetime): When job was created. + start_time (datetime, optional): When job started. + end_time (datetime, optional): When job completed. + update_time (datetime): Last update time. + labels (dict[str, str], optional): Job labels. + tuning_data_stats (TuningDataStats, optional): Training data statistics. + error (JobError, optional): Error if job failed. + """ + name: str + base_model: str + tuned_model: Optional[TunedModel] = None + state: JobState + create_time: datetime + start_time: Optional[datetime] = None + end_time: Optional[datetime] = None + update_time: datetime + labels: Optional[dict[str, str]] = None + tuning_data_stats: Optional[TuningDataStats] = None + error: Optional[JobError] = None + +class TunedModel: + """ + Information about tuned model. + + Attributes: + model (str): Model resource name for use in generation requests. + endpoint (str, optional): Endpoint for the tuned model. + display_name (str, optional): Display name. + """ + model: str + endpoint: Optional[str] = None + display_name: Optional[str] = None + +class TuningDataStats: + """ + Statistics about tuning data. + + Attributes: + training_dataset_size (int, optional): Number of training examples. + validation_dataset_size (int, optional): Number of validation examples. + total_tuning_character_count (int, optional): Total characters in training data. + total_billable_character_count (int, optional): Billable characters. + """ + training_dataset_size: Optional[int] = None + validation_dataset_size: Optional[int] = None + total_tuning_character_count: Optional[int] = None + total_billable_character_count: Optional[int] = None + +class JobState(Enum): + """Tuning job states.""" + JOB_STATE_UNSPECIFIED = 'JOB_STATE_UNSPECIFIED' + JOB_STATE_QUEUED = 'JOB_STATE_QUEUED' + JOB_STATE_PENDING = 'JOB_STATE_PENDING' + JOB_STATE_RUNNING = 'JOB_STATE_RUNNING' + JOB_STATE_SUCCEEDED = 'JOB_STATE_SUCCEEDED' + JOB_STATE_FAILED = 'JOB_STATE_FAILED' + JOB_STATE_CANCELLING = 'JOB_STATE_CANCELLING' + JOB_STATE_CANCELLED = 'JOB_STATE_CANCELLED' + JOB_STATE_PAUSED = 'JOB_STATE_PAUSED' + +class AdapterSize(Enum): + """Adapter sizes for efficient tuning.""" + ADAPTER_SIZE_UNSPECIFIED = 'ADAPTER_SIZE_UNSPECIFIED' + ADAPTER_SIZE_ONE = 'ADAPTER_SIZE_ONE' + ADAPTER_SIZE_FOUR = 'ADAPTER_SIZE_FOUR' + ADAPTER_SIZE_EIGHT = 'ADAPTER_SIZE_EIGHT' + ADAPTER_SIZE_SIXTEEN = 'ADAPTER_SIZE_SIXTEEN' + ADAPTER_SIZE_THIRTY_TWO = 'ADAPTER_SIZE_THIRTY_TWO' + +class JobError: + """ + Job error information. + + Attributes: + code (int): Error code. + message (str): Error message. + details (list[dict], optional): Error details. + """ + code: int + message: str + details: Optional[list[dict]] = None + +# Pager types +class Pager[T]: + """Synchronous pager.""" + page: list[T] + def next_page(self) -> None: ... + def __iter__(self) -> Iterator[T]: ... + +class AsyncPager[T]: + """Asynchronous pager.""" + page: list[T] + async def next_page(self) -> None: ... + async def __aiter__(self) -> AsyncIterator[T]: ... +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md b/.tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md new file mode 100644 index 000000000..ea1c8f51d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md @@ -0,0 +1,319 @@ +# Video Generation + +Generate videos from text prompts, images, or existing videos using Veo models. Video generation is a long-running operation that returns immediately with an operation handle that can be polled for completion status. + +## Capabilities + +### Generate Videos + +Generate videos from text prompts, optionally with reference images or videos. Returns a long-running operation that must be polled for completion. + +```python { .api } +def generate_videos( + *, + model: str, + prompt: Optional[str] = None, + image: Optional[Image] = None, + video: Optional[Video] = None, + config: Optional[GenerateVideosConfig] = None +) -> GenerateVideosOperation: + """ + Generate videos (returns long-running operation). + + Parameters: + model (str): Model identifier (e.g., 'veo-2.0-generate-001', 'veo-001'). + prompt (str, optional): Text description of the video to generate. At least one + of prompt, image, or video must be provided. + image (Image, optional): Reference image for image-to-video generation. + video (Video, optional): Reference video for video-to-video generation. + config (GenerateVideosConfig, optional): Generation configuration including: + - aspect_ratio: Video aspect ratio ('16:9', '9:16', '1:1') + - duration: Video duration in seconds + - negative_prompt: What to avoid + - safety_filter_level: Safety filtering level + - person_generation: Control person generation + - compressed: Use compressed output format + + Returns: + GenerateVideosOperation: Long-running operation handle. Use client.operations.get() + to poll for completion and retrieve the generated videos. + + Raises: + ClientError: For client errors (4xx status codes) + ServerError: For server errors (5xx status codes) + """ + ... + +async def generate_videos( + *, + model: str, + prompt: Optional[str] = None, + image: Optional[Image] = None, + video: Optional[Video] = None, + config: Optional[GenerateVideosConfig] = None +) -> GenerateVideosOperation: + """Async version of generate_videos.""" + ... +``` + +**Usage Example - Text to Video:** + +```python +import time +from google.genai import Client +from google.genai.types import GenerateVideosConfig + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Start video generation +config = GenerateVideosConfig( + aspect_ratio='16:9', + duration=5, # 5 seconds + negative_prompt='blurry, low quality' +) + +operation = client.models.generate_videos( + model='veo-2.0-generate-001', + prompt='A dog running through a field of flowers at sunset', + config=config +) + +print(f"Operation started: {operation.name}") + +# Poll for completion +while True: + operation = client.operations.get(operation) + + if operation.done: + if operation.error: + print(f"Error: {operation.error}") + else: + response = operation.response + print(f"Generated {len(response.generated_videos)} video(s)") + + # Save videos + for i, video in enumerate(response.generated_videos): + with open(f'generated_{i}.mp4', 'wb') as f: + f.write(video.video.data) + break + + print("Still generating...") + time.sleep(10) # Wait 10 seconds before polling again +``` + +**Usage Example - Image to Video:** + +```python +from google.genai import Client +from google.genai.types import Image, GenerateVideosConfig + +client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + +# Load reference image +image = Image.from_file('landscape.jpg') + +config = GenerateVideosConfig( + aspect_ratio='16:9', + duration=8 +) + +# Generate video from image +operation = client.models.generate_videos( + model='veo-2.0-generate-001', + prompt='Camera slowly pans across the landscape', + image=image, + config=config +) + +print(f"Operation: {operation.name}") +# Poll operation.done until complete +``` + +**Usage Example - Async with Polling:** + +```python +import asyncio +from google.genai import Client + +async def generate_and_wait(): + client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') + + # Start generation + operation = await client.aio.models.generate_videos( + model='veo-2.0-generate-001', + prompt='A cat playing with a ball of yarn' + ) + + print(f"Started operation: {operation.name}") + + # Poll until complete + while not operation.done: + await asyncio.sleep(10) + operation = await client.aio.operations.get(operation) + print("Checking status...") + + if operation.error: + print(f"Error: {operation.error}") + else: + print(f"Success! {len(operation.response.generated_videos)} videos generated") + +asyncio.run(generate_and_wait()) +``` + +## Types + +```python { .api } +from typing import Optional, Literal, TypedDict +from enum import Enum + +# Configuration types +class GenerateVideosConfig: + """ + Configuration for video generation. + + Attributes: + aspect_ratio (str, optional): Video aspect ratio. Options: '16:9', '9:16', '1:1'. + Default varies by model. + duration (int, optional): Video duration in seconds. Valid range depends on model. + Typical: 4-8 seconds. + negative_prompt (str, optional): What to avoid in the video. + safety_filter_level (str, optional): Safety filtering level ('block_most', 'block_some', 'block_few'). + person_generation (PersonGeneration, optional): Control person generation in video. + compressed (bool, optional): Use compressed output format for smaller file size. + generation_mode (str, optional): Generation mode (e.g., 'fast', 'quality'). + """ + aspect_ratio: Optional[str] = None + duration: Optional[int] = None + negative_prompt: Optional[str] = None + safety_filter_level: Optional[str] = None + person_generation: Optional[PersonGeneration] = None + compressed: Optional[bool] = None + generation_mode: Optional[str] = None + +# Response types +class GenerateVideosResponse: + """ + Response from completed video generation. + + Accessed via operation.response after operation completes. + + Attributes: + generated_videos (list[GeneratedVideo]): Generated videos with metadata. + rai_media_filtered_count (int, optional): Number of videos filtered by safety. + rai_media_filtered_reasons (list[str], optional): Safety filtering reasons. + """ + generated_videos: list[GeneratedVideo] + rai_media_filtered_count: Optional[int] = None + rai_media_filtered_reasons: Optional[list[str]] = None + +class GeneratedVideo: + """ + Generated video with metadata. + + Attributes: + video (Video): Video object with data. + generation_seed (int, optional): Seed used for generation (for reproducibility). + """ + video: Video + generation_seed: Optional[int] = None + +# Operation types +class GenerateVideosOperation: + """ + Long-running operation for video generation. + + Attributes: + name (str): Operation name/identifier for polling. + done (bool): Whether operation is complete. + error (OperationError, optional): Error if operation failed. + response (GenerateVideosResponse, optional): Response if operation succeeded. + metadata (dict, optional): Operation metadata including progress. + """ + name: str + done: bool + error: Optional[OperationError] = None + response: Optional[GenerateVideosResponse] = None + metadata: Optional[dict] = None + +class OperationError: + """ + Operation error information. + + Attributes: + code (int): Error code. + message (str): Error message. + details (list, optional): Additional error details. + """ + code: int + message: str + details: Optional[list] = None + +# Input types +class Video: + """ + Video data supporting multiple formats. + + Can be created from: + - File: Video.from_file('path.mp4') + - URL: Video.from_url('https://...') + - Bytes: Video.from_bytes(data, mime_type='video/mp4') + - FileData: Video(file_data=FileData(...)) + + Attributes: + data (bytes, optional): Video binary data. + mime_type (str, optional): MIME type (e.g., 'video/mp4'). + file_data (FileData, optional): Reference to uploaded file. + """ + data: Optional[bytes] = None + mime_type: Optional[str] = None + file_data: Optional[FileData] = None + + @staticmethod + def from_file(path: str) -> Video: + """Load video from file path.""" + ... + + @staticmethod + def from_url(url: str) -> Video: + """Load video from URL.""" + ... + + @staticmethod + def from_bytes(data: bytes, mime_type: str) -> Video: + """Create video from bytes.""" + ... + +class Image: + """Image data for image-to-video generation.""" + @staticmethod + def from_file(path: str) -> Image: ... + +class FileData: + """ + Reference to uploaded file. + + Attributes: + file_uri (str): URI (e.g., 'gs://bucket/file') + mime_type (str): MIME type + """ + file_uri: str + mime_type: str + +# Enum types +class PersonGeneration(Enum): + """Person generation control.""" + DONT_ALLOW = 'dont_allow' + ALLOW_ADULT = 'allow_adult' + ALLOW_ALL = 'allow_all' + +# TypedDict variants +class GenerateVideosConfigDict(TypedDict, total=False): + """TypedDict variant of GenerateVideosConfig.""" + aspect_ratio: str + duration: int + negative_prompt: str + safety_filter_level: str + person_generation: PersonGeneration + compressed: bool + generation_mode: str +``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/tile.json b/.tessl/tiles/tessl/pypi-google-genai/tile.json new file mode 100644 index 000000000..53b88bff9 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-google-genai/tile.json @@ -0,0 +1,8 @@ +{ + "name": "tessl/pypi-google-genai", + "version": "1.51.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/google-genai@1.51.0", + "summary": "GenAI Python SDK for Google's generative models supporting both Gemini Developer API and Vertex AI APIs", + "private": false +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/compute-resources.md b/.tessl/tiles/tessl/pypi-modal/docs/compute-resources.md new file mode 100644 index 000000000..2c597a024 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/compute-resources.md @@ -0,0 +1,490 @@ +# Compute Resources + +Functions and classes that execute serverless workloads in Modal's cloud infrastructure, including interactive sandboxes for development and debugging. + +## Capabilities + +### Function Class + +Represents a deployed serverless function that can be called remotely, locally, or in parallel across multiple inputs. + +```python { .api } +class Function: + def remote(self, *args, **kwargs): + """ + Call the function remotely in Modal's cloud. + + Parameters: + - *args: Positional arguments to pass to the function + - **kwargs: Keyword arguments to pass to the function + + Returns: + Result of the function execution + """ + + def local(self, *args, **kwargs): + """ + Call the function locally (useful for testing). + + Parameters: + - *args: Positional arguments to pass to the function + - **kwargs: Keyword arguments to pass to the function + + Returns: + Result of the function execution + """ + + def map( + self, + inputs, + order_outputs: bool = True, + return_exceptions: bool = False + ): + """ + Map the function over a list of inputs in parallel. + + Parameters: + - inputs: Iterable of inputs to map over + - order_outputs: Whether to return outputs in input order + - return_exceptions: Include exceptions in results instead of raising + + Returns: + List of results + """ + + def spawn(self, *args, **kwargs): + """ + Spawn the function asynchronously without waiting for result. + + Parameters: + - *args: Positional arguments to pass to the function + - **kwargs: Keyword arguments to pass to the function + + Returns: + FunctionCall object for retrieving result later + """ + + @classmethod + def from_name( + cls, + app_name: str, + tag: str = None, + namespace: str = None, + environment_name: str = None + ): + """ + Look up a deployed function by name. + + Parameters: + - app_name: Name of the app containing the function + - tag: Optional deployment tag + - namespace: Namespace to search in + - environment_name: Environment name + + Returns: + Function object + """ +``` + +### FunctionCall Class + +Represents an executing or executed function call, providing methods to retrieve results and manage execution. + +```python { .api } +class FunctionCall: + def get(self, timeout: float = None): + """ + Get the result of the function call, blocking until completion. + + Parameters: + - timeout: Maximum time to wait for result in seconds + + Returns: + Result of the function call + + Raises: + TimeoutError: If timeout is exceeded + """ + + def cancel(self): + """ + Cancel the running function call. + + Returns: + True if cancellation was successful + """ + + def is_finished(self) -> bool: + """ + Check if the function call has completed. + + Returns: + True if finished, False otherwise + """ + + @property + def object_id(self) -> str: + """ + Get the unique ID of this function call. + + Returns: + Function call ID string + """ +``` + +### Cls Class + +Serverless class for stateful compute with lifecycle methods, enabling persistent state across method calls. + +```python { .api } +class Cls: + @classmethod + def from_name( + cls, + app_name: str, + tag: str = None, + namespace: str = None, + environment_name: str = None + ): + """ + Look up a deployed class by name. + + Parameters: + - app_name: Name of the app containing the class + - tag: Optional deployment tag + - namespace: Namespace to search in + - environment_name: Environment name + + Returns: + Cls object + """ + + def lookup(self, method_name: str): + """ + Look up a specific method of the class. + + Parameters: + - method_name: Name of the method to look up + + Returns: + Method object that can be called remotely + """ +``` + +### Sandbox Class + +Interactive compute environment for development, debugging, and ad-hoc computation. + +```python { .api } +class Sandbox: + @classmethod + def create( + cls, + image: Image = None, + secrets: list[Secret] = None, + volumes: dict[str, Volume] = None, + mounts: list[Mount] = None, + memory: int = None, + cpu: float = None, + gpu: GPU_T = None, + timeout: int = None, + workdir: str = "/root", + **kwargs + ): + """ + Create a new interactive sandbox. + + Parameters: + - image: Container image for the sandbox + - secrets: List of secrets to inject + - volumes: Dictionary mapping paths to volumes + - mounts: List of mount objects + - memory: Memory limit in MB + - cpu: CPU allocation + - gpu: GPU configuration + - timeout: Sandbox timeout in seconds + - workdir: Working directory + + Returns: + Sandbox instance + """ + + def exec( + self, + command: str, + workdir: str = None, + secrets: list[Secret] = None, + **kwargs + ): + """ + Execute a command in the sandbox. + + Parameters: + - command: Shell command to execute + - workdir: Working directory for command + - secrets: Additional secrets for this command + + Returns: + Command execution result + """ + + def terminate(self): + """ + Terminate the sandbox, cleaning up all resources. + """ + + def tunnel(self, port: int): + """ + Create a tunnel to a port in the sandbox. + + Parameters: + - port: Port number to tunnel to + + Returns: + Tunnel URL for accessing the port + """ + + @property + def object_id(self) -> str: + """ + Get the unique ID of this sandbox. + + Returns: + Sandbox ID string + """ +``` + +### SandboxSnapshot Class + +Snapshot of a sandbox environment that can be reused to create new sandboxes or deploy as container images. + +```python { .api } +class SandboxSnapshot: + @classmethod + def create( + cls, + sandbox: Sandbox, + label: str = None + ): + """ + Create a snapshot from an existing sandbox. + + Parameters: + - sandbox: Source sandbox to snapshot + - label: Optional label for the snapshot + + Returns: + SandboxSnapshot instance + """ + + def deploy(self, name: str): + """ + Deploy the snapshot as a container image. + + Parameters: + - name: Name for the deployed image + + Returns: + Deployed image reference + """ + + @classmethod + def from_name( + cls, + label: str, + namespace: str = None + ): + """ + Look up a snapshot by label. + + Parameters: + - label: Snapshot label + - namespace: Optional namespace + + Returns: + SandboxSnapshot instance + """ +``` + +## Usage Examples + +### Basic Function Usage + +```python +import modal + +app = modal.App("compute-example") + +@app.function() +def fibonacci(n: int) -> int: + if n <= 1: + return n + return fibonacci(n-1) + fibonacci(n-2) + +@app.local_entrypoint() +def main(): + # Call function remotely + result = fibonacci.remote(10) + print(f"Fibonacci(10) = {result}") + + # Map over multiple inputs + results = fibonacci.map([1, 2, 3, 4, 5]) + print(f"Results: {results}") + + # Spawn async call + call = fibonacci.spawn(15) + # Do other work... + result = call.get() + print(f"Async result: {result}") +``` + +### Stateful Class Example + +```python +import modal +from modal import enter, exit, method + +app = modal.App("stateful-service") + +@app.cls() +class DatabaseService: + @enter() + def setup(self): + # Initialize database connection + self.connection = create_db_connection() + self.cache = {} + + @exit() + def cleanup(self): + # Clean up resources + self.connection.close() + + @method() + def query(self, sql: str): + if sql in self.cache: + return self.cache[sql] + + result = self.connection.execute(sql) + self.cache[sql] = result + return result + + @method() + def insert(self, table: str, data: dict): + return self.connection.insert(table, data) + +@app.local_entrypoint() +def main(): + db = DatabaseService() + + # Insert data + db.insert.remote("users", {"name": "Alice", "email": "alice@example.com"}) + + # Query data (uses caching) + users = db.query.remote("SELECT * FROM users") + print(users) +``` + +### Interactive Sandbox + +```python +import modal + +# Create a sandbox with custom environment +sandbox = modal.Sandbox.create( + image=modal.Image.debian_slim().pip_install("pandas", "matplotlib"), + timeout=3600 # 1 hour +) + +# Execute commands +result = sandbox.exec("python -c 'import pandas; print(pandas.__version__)'") +print(f"Pandas version: {result.stdout}") + +# Run a data analysis script +analysis_code = """ +import pandas as pd +import matplotlib.pyplot as plt + +# Create sample data +data = {'x': range(10), 'y': [i**2 for i in range(10)]} +df = pd.DataFrame(data) + +# Create plot +plt.figure(figsize=(8, 6)) +plt.plot(df['x'], df['y']) +plt.savefig('/tmp/plot.png') +plt.close() + +print("Analysis complete") +""" + +result = sandbox.exec(f"python -c \"{analysis_code}\"") +print(result.stdout) + +# Terminate when done +sandbox.terminate() +``` + +### Sandbox Snapshots + +```python +import modal + +# Create and configure a sandbox +sandbox = modal.Sandbox.create( + image=modal.Image.debian_slim() +) + +# Install custom software +sandbox.exec("apt-get update && apt-get install -y git curl") +sandbox.exec("pip install numpy scipy scikit-learn") + +# Create a snapshot +snapshot = modal.SandboxSnapshot.create(sandbox, label="ml-environment") + +# Use the snapshot to create new sandboxes +new_sandbox = modal.Sandbox.create(image=snapshot) + +# Or deploy as an image for functions +app = modal.App("ml-app") + +@app.function(image=snapshot) +def ml_computation(data): + import numpy as np + from sklearn.linear_model import LinearRegression + + # ML computation using pre-installed libraries + model = LinearRegression() + return model.fit(data['X'], data['y']).predict(data['X_test']) +``` + +### Parallel Processing + +```python +import modal + +app = modal.App("parallel-processing") + +@app.function() +def process_batch(batch_data: list) -> dict: + # Process a batch of data + results = {} + for item in batch_data: + results[item['id']] = expensive_computation(item['data']) + return results + +@app.local_entrypoint() +def main(): + # Prepare data batches + all_data = [{'id': i, 'data': f"data_{i}"} for i in range(1000)] + batches = [all_data[i:i+100] for i in range(0, len(all_data), 100)] + + # Process batches in parallel + batch_results = process_batch.map(batches) + + # Combine results + final_results = {} + for batch_result in batch_results: + final_results.update(batch_result) + + print(f"Processed {len(final_results)} items") +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/container-images.md b/.tessl/tiles/tessl/pypi-modal/docs/container-images.md new file mode 100644 index 000000000..b6127fecc --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/container-images.md @@ -0,0 +1,503 @@ +# Container Images + +Container image definitions that specify the runtime environment for functions and classes. Images define the operating system, Python version, packages, and system dependencies needed for your code to run. + +## Capabilities + +### Image Class + +Defines container environments with pre-installed dependencies, custom configurations, and optimized builds for serverless execution. + +```python { .api } +class Image: + @classmethod + def debian_slim( + cls, + python_version: str = None, + builder_version: str = None + ): + """ + Create a Debian slim base image with Python. + + Parameters: + - python_version: Python version (e.g., "3.11", "3.12"). Defaults to local version. + - builder_version: Image builder version for reproducible builds + + Returns: + Image instance with Debian slim base + """ + + @classmethod + def from_registry( + cls, + tag: str, + secret: Secret = None, + setup_dockerfile_commands: list[str] = None + ): + """ + Create an image from a container registry (Docker Hub, ECR, etc.). + + Parameters: + - tag: Full image tag (e.g., "python:3.11-slim", "ubuntu:22.04") + - secret: Secret for private registry authentication + - setup_dockerfile_commands: Additional setup commands to run + + Returns: + Image instance based on registry image + """ + + def pip_install( + self, + *packages: str, + find_links: str = None, + index_url: str = None, + extra_index_url: str = None, + pre: bool = False, + force_build: bool = False + ): + """ + Install Python packages using pip. + + Parameters: + - *packages: Package names, optionally with version specifiers + - find_links: Additional URLs to search for packages + - index_url: Base URL of package index + - extra_index_url: Extra URLs of package indexes + - pre: Include pre-release versions + - force_build: Force rebuild even if cached + + Returns: + New Image instance with packages installed + """ + + def poetry_install_from_file( + self, + poetry_pyproject_toml: str, + poetry_lock_file: str = None, + ignore_lockfile: bool = False + ): + """ + Install dependencies from Poetry configuration files. + + Parameters: + - poetry_pyproject_toml: Path to pyproject.toml file + - poetry_lock_file: Path to poetry.lock file + - ignore_lockfile: Install without using lockfile + + Returns: + New Image instance with Poetry dependencies + """ + + def run_commands( + self, + *commands: str, + secrets: list[Secret] = None, + gpu: str = None + ): + """ + Run shell commands during image build. + + Parameters: + - *commands: Shell commands to execute + - secrets: Secrets available during build + - gpu: GPU type for build environment + + Returns: + New Image instance with commands executed + """ + + def apt_install( + self, + *packages: str, + force_build: bool = False + ): + """ + Install system packages using apt (Debian/Ubuntu). + + Parameters: + - *packages: Package names to install + - force_build: Force rebuild even if cached + + Returns: + New Image instance with system packages + """ + + def copy_local_file( + self, + local_path: str, + remote_path: str = None + ): + """ + Copy a local file into the image. + + Parameters: + - local_path: Path to local file or directory + - remote_path: Destination path in image. Defaults to same path. + + Returns: + New Image instance with file copied + """ + + def copy_local_dir( + self, + local_path: str, + remote_path: str = None, + condition: callable = None + ): + """ + Copy a local directory into the image. + + Parameters: + - local_path: Path to local directory + - remote_path: Destination path in image + - condition: Function to filter which files to copy + + Returns: + New Image instance with directory copied + """ + + def add_python_packages( + self, + *python_packages: str, + force_build: bool = False + ): + """ + Add Python packages from local wheel files or directories. + + Parameters: + - *python_packages: Paths to wheel files or package directories + - force_build: Force rebuild even if cached + + Returns: + New Image instance with packages added + """ + + def dockerfile_commands( + self, + dockerfile_commands: list[str], + secrets: list[Secret] = None, + gpu: str = None, + context_files: dict = None + ): + """ + Execute Dockerfile commands during build. + + Parameters: + - dockerfile_commands: List of Dockerfile commands (RUN, COPY, etc.) + - secrets: Build secrets + - gpu: GPU type for build + - context_files: Files to include in build context + + Returns: + New Image instance with Dockerfile commands applied + """ + + def env(self, **env_vars: str): + """ + Set environment variables in the image. + + Parameters: + - **env_vars: Environment variables as keyword arguments + + Returns: + New Image instance with environment variables set + """ + + def workdir(self, path: str): + """ + Set the working directory for the image. + + Parameters: + - path: Working directory path + + Returns: + New Image instance with working directory set + """ +``` + +## Usage Examples + +### Basic Python Environment + +```python +import modal + +# Simple Python environment +image = modal.Image.debian_slim(python_version="3.11").pip_install( + "requests", + "beautifulsoup4", + "pandas==2.0.0" +) + +app = modal.App("web-scraper") + +@app.function(image=image) +def scrape_and_analyze(url: str): + import requests + import pandas as pd + from bs4 import BeautifulSoup + + response = requests.get(url) + soup = BeautifulSoup(response.content, 'html.parser') + + # Extract and analyze data + links = [a.get('href') for a in soup.find_all('a', href=True)] + df = pd.DataFrame({'links': links}) + + return { + 'total_links': len(links), + 'unique_domains': len(df['links'].apply(lambda x: x.split('/')[2] if x.startswith('http') else None).dropna().unique()) + } +``` + +### Data Science Environment + +```python +import modal + +# Comprehensive data science environment +ds_image = ( + modal.Image.debian_slim(python_version="3.11") + .apt_install("git", "curl", "build-essential") + .pip_install( + "numpy", + "pandas", + "scikit-learn", + "matplotlib", + "seaborn", + "jupyter", + "plotly", + "scipy" + ) + .run_commands( + "pip install --no-deps lightgbm", # Custom installation + "mkdir -p /opt/data" + ) + .env( + DATA_PATH="/opt/data", + PYTHONPATH="/opt/models" + ) +) + +app = modal.App("ml-pipeline") + +@app.function(image=ds_image) +def train_model(dataset_path: str): + import pandas as pd + from sklearn.ensemble import RandomForestClassifier + from sklearn.model_selection import train_test_split + from sklearn.metrics import accuracy_score + + # Load and prepare data + df = pd.read_csv(dataset_path) + X = df.drop('target', axis=1) + y = df['target'] + + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) + + # Train model + model = RandomForestClassifier(n_estimators=100) + model.fit(X_train, y_train) + + # Evaluate + predictions = model.predict(X_test) + accuracy = accuracy_score(y_test, predictions) + + return {"accuracy": accuracy, "model_type": "RandomForest"} +``` + +### Custom Base Image + +```python +import modal + +# Using a custom base image from Docker Hub +custom_image = ( + modal.Image.from_registry("nvidia/cuda:11.8-devel-ubuntu22.04") + .apt_install("python3", "python3-pip", "python3-dev") + .run_commands( + "ln -s /usr/bin/python3 /usr/bin/python", + "pip install --upgrade pip" + ) + .pip_install("torch", "torchvision", "transformers") + .env(CUDA_VISIBLE_DEVICES="0") +) + +app = modal.App("gpu-ml") + +@app.function( + image=custom_image, + gpu="T4" # GPU required for this function +) +def run_inference(model_name: str, input_text: str): + import torch + from transformers import AutoTokenizer, AutoModel + + # Load model and tokenizer + tokenizer = AutoTokenizer.from_pretrained(model_name) + model = AutoModel.from_pretrained(model_name) + + # Tokenize and run inference + inputs = tokenizer(input_text, return_tensors="pt") + with torch.no_grad(): + outputs = model(**inputs) + + return { + "embeddings_shape": outputs.last_hidden_state.shape, + "mean_embedding": outputs.last_hidden_state.mean().item() + } +``` + +### Development Environment with Local Code + +```python +import modal + +# Development image with local code and dependencies +dev_image = ( + modal.Image.debian_slim() + .pip_install("fastapi", "uvicorn", "sqlalchemy", "pytest") + .copy_local_dir("./src", "/app/src") # Copy local source code + .copy_local_file("requirements.txt", "/app/requirements.txt") + .run_commands("pip install -r /app/requirements.txt") + .workdir("/app") +) + +app = modal.App("development") + +@app.function(image=dev_image) +def run_tests(): + import subprocess + + # Run tests using the copied code + result = subprocess.run( + ["python", "-m", "pytest", "src/tests/", "-v"], + capture_output=True, + text=True, + cwd="/app" + ) + + return { + "exit_code": result.returncode, + "stdout": result.stdout, + "stderr": result.stderr + } +``` + +### Poetry-based Project + +```python +import modal + +# Using Poetry for dependency management +poetry_image = ( + modal.Image.debian_slim() + .apt_install("curl") + .run_commands( + "curl -sSL https://install.python-poetry.org | python3 -", + "export PATH=\"/root/.local/bin:$PATH\"" + ) + .poetry_install_from_file( + "pyproject.toml", + "poetry.lock" + ) + .copy_local_dir("./my_package", "/app/my_package") + .workdir("/app") +) + +app = modal.App("poetry-app") + +@app.function(image=poetry_image) +def process_with_poetry_deps(): + # Use dependencies installed via Poetry + from my_package import main_module + return main_module.process_data() +``` + +### Multi-stage Build + +```python +import modal + +# Complex multi-stage build with custom compilation +build_image = ( + modal.Image.debian_slim() + .apt_install( + "build-essential", + "cmake", + "git", + "pkg-config", + "libssl-dev" + ) + .run_commands( + # Clone and build custom library + "git clone https://github.com/example/custom-lib.git /tmp/custom-lib", + "cd /tmp/custom-lib && mkdir build && cd build", + "cmake .. && make -j$(nproc) && make install" + ) + .pip_install("numpy", "cython") + .copy_local_file("setup.py", "/app/setup.py") + .copy_local_dir("./src", "/app/src") + .run_commands( + "cd /app && python setup.py build_ext --inplace", + "pip install -e ." + ) + .apt_install("--purge", "build-essential", "cmake") # Clean up build tools + .run_commands("apt-get autoremove -y && apt-get clean") +) + +app = modal.App("optimized-build") + +@app.function(image=build_image) +def run_optimized_computation(data): + # Use the custom-built optimized library + import my_optimized_package + return my_optimized_package.fast_compute(data) +``` + +## Common Patterns + +### Environment Variables and Configuration + +```python +config_image = ( + modal.Image.debian_slim() + .pip_install("python-dotenv", "pydantic") + .env( + ENVIRONMENT="production", + LOG_LEVEL="INFO", + API_TIMEOUT="30" + ) + .copy_local_file(".env.production", "/app/.env") +) +``` + +### Caching for Faster Builds + +```python +# Use force_build=False (default) to enable caching +cached_image = ( + modal.Image.debian_slim() + .pip_install("numpy", "pandas", force_build=False) # Will be cached + .copy_local_file("requirements.txt") # This will trigger rebuild if file changes + .pip_install("-r", "requirements.txt") +) +``` + +### Secrets in Build Process + +```python +build_secret = modal.Secret.from_dict({ + "GITHUB_TOKEN": "ghp_...", + "PRIVATE_REGISTRY_TOKEN": "..." +}) + +secure_image = ( + modal.Image.debian_slim() + .run_commands( + "git clone https://${GITHUB_TOKEN}@github.com/private/repo.git /app", + secrets=[build_secret] + ) + .pip_install("--extra-index-url", "https://token:${PRIVATE_REGISTRY_TOKEN}@pypi.private.com/simple/", "private-package", secrets=[build_secret]) +) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/core-application-client.md b/.tessl/tiles/tessl/pypi-modal/docs/core-application-client.md new file mode 100644 index 000000000..34a81170a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/core-application-client.md @@ -0,0 +1,284 @@ +# Core Application & Client + +Primary interfaces for defining applications and managing authentication and object lookup with Modal's cloud platform. + +## Capabilities + +### App Class + +Main application container for defining and deploying Modal functions and resources. All serverless functions, classes, and resources are defined within the context of an App. + +```python { .api } +class App: + def __init__(self, name: str = None): + """ + Create a new Modal application. + + Parameters: + - name: Optional name for the app. If not provided, a name will be generated. + """ + + def function( + self, + image: Optional["Image"] = None, + schedule: Optional[Union["Cron", "Period"]] = None, + secrets: Optional[list["Secret"]] = None, + volumes: Optional[dict[str, "Volume"]] = None, + network_file_systems: Optional[dict[str, "NetworkFileSystem"]] = None, + cloud_bucket_mounts: Optional[dict[str, "CloudBucketMount"]] = None, + memory: Optional[int] = None, + cpu: Optional[float] = None, + gpu: Optional[str] = None, + timeout: Optional[int] = None, + retries: Optional[Union[int, "Retries"]] = None, + concurrency_limit: Optional[int] = None, + allow_concurrent_inputs: Optional[int] = None, + container_idle_timeout: Optional[int] = None, + keep_warm: Optional[int] = None, + **kwargs + ): + """ + Decorator to define a serverless function within the app. + + Parameters: + - image: Container image to run the function in + - schedule: Schedule for automatic execution (Cron or Period) + - secrets: List of secrets to inject as environment variables + - volumes: Dictionary mapping mount paths to Volume objects + - network_file_systems: Dictionary mapping mount paths to NetworkFileSystem objects + - cloud_bucket_mounts: Dictionary mapping mount paths to CloudBucketMount objects + - memory: Memory limit in MB + - cpu: CPU allocation (fractional values supported) + - gpu: GPU specification string (e.g., "any", "a10g", "h100") + - timeout: Function timeout in seconds + - retries: Retry policy for failed executions + - concurrency_limit: Maximum concurrent executions + - allow_concurrent_inputs: Allow concurrent inputs per container + - container_idle_timeout: How long to keep containers warm + - keep_warm: Number of containers to keep warm + + Returns: + Function decorator + """ + + def cls( + self, + image: Image = None, + secrets: list[Secret] = None, + volumes: dict[str, Volume] = None, + mounts: list[Mount] = None, + memory: int = None, + cpu: float = None, + gpu: GPU_T = None, + timeout: int = None, + retries: Retries = None, + concurrency_limit: int = None, + container_idle_timeout: int = None, + keep_warm: int = None, + **kwargs + ): + """ + Decorator to define a serverless class within the app. + + Parameters: + - image: Container image to run the class in + - secrets: List of secrets to inject as environment variables + - volumes: Dictionary mapping mount paths to Volume objects + - mounts: List of mount objects for code and data + - memory: Memory limit in MB + - cpu: CPU allocation + - gpu: GPU configuration + - timeout: Method timeout in seconds + - retries: Retry policy for failed method calls + - concurrency_limit: Maximum concurrent class instances + - container_idle_timeout: How long to keep containers warm + - keep_warm: Number of containers to keep warm + + Returns: + Class decorator + """ + + def local_entrypoint(self): + """ + Decorator to define a local entry point that can call remote functions. + + The decorated function will run locally and can invoke remote functions + defined in the same app. + + Returns: + Function decorator + """ + + def deploy(self, name: str = None): + """ + Deploy the app to Modal cloud. + + Parameters: + - name: Optional deployment name + + Returns: + Deployed app handle + """ + + def run(self, detach: bool = False): + """ + Run the app, executing the local entrypoint if defined. + + Parameters: + - detach: Run in detached mode + """ + + def stop(self): + """ + Stop a running app. + """ + + def list_objects(self): + """ + List all objects (functions, classes, etc.) defined in the app. + + Returns: + List of app objects + """ +``` + +### Client Class + +Client for interacting with Modal's API, managing authentication, and looking up deployed objects. + +```python { .api } +class Client: + @classmethod + def from_env( + cls, + profile: str = None, + token_id: str = None, + token_secret: str = None + ): + """ + Create a client from environment variables or profile. + + Parameters: + - profile: Named profile to use for authentication + - token_id: Override token ID from environment + - token_secret: Override token secret from environment + + Returns: + Authenticated Client instance + """ + + def lookup( + self, + label: str, + namespace: str = None, + create_if_missing: bool = False + ): + """ + Look up a deployed object by label. + + Parameters: + - label: Label of the object to look up + - namespace: Namespace to search in + - create_if_missing: Create the object if it doesn't exist + + Returns: + The deployed object + """ + + def list(self, namespace: str = None): + """ + List objects in the account. + + Parameters: + - namespace: Optional namespace to filter by + + Returns: + List of deployed objects + """ +``` + +## Usage Examples + +### Basic App Definition + +```python +import modal + +# Create an app +app = modal.App("my-application") + +# Define a simple function +@app.function() +def hello(name: str) -> str: + return f"Hello, {name}!" + +# Local entrypoint to test the function +@app.local_entrypoint() +def main(): + result = hello.remote("World") + print(result) # "Hello, World!" +``` + +### App with Custom Configuration + +```python +import modal + +app = modal.App("data-processor") + +# Custom image with dependencies +image = modal.Image.debian_slim().pip_install("pandas", "numpy") + +# Function with resource configuration +@app.function( + image=image, + memory=2048, # 2GB memory + timeout=600, # 10 minute timeout + secrets=[modal.Secret.from_name("api-key")], + volumes={"/data": modal.Volume.from_name("dataset")} +) +def process_data(filename: str): + import pandas as pd + df = pd.read_csv(f"/data/{filename}") + return df.groupby('category').sum().to_dict() + +@app.local_entrypoint() +def main(): + result = process_data.remote("sales_data.csv") + print(result) +``` + +### Client Usage + +```python +import modal + +# Create client from environment +client = modal.Client.from_env() + +# Look up a deployed function +my_function = client.lookup("my-function") + +# Call the remote function +result = my_function.remote("input_data") +``` + +### Deployment + +```python +import modal + +app = modal.App("production-app") + +@app.function() +def my_function(): + return "Hello from production!" + +# Deploy to Modal cloud +if __name__ == "__main__": + # Deploy the app + app.deploy("v1.0") + + # Or run locally for development + # app.run() +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md b/.tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md new file mode 100644 index 000000000..f49bff702 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md @@ -0,0 +1,418 @@ +# Function Decorators & Helpers + +Modal provides specialized decorators and helper functions for enhancing function behavior, defining class lifecycle methods, enabling batched execution, and controlling concurrency. These tools allow fine-grained control over how functions execute in the Modal environment. + +## Capabilities + +### Method Decorator + +Decorator for defining methods within Modal classes, enabling stateful serverless computing with shared instance state. + +```python { .api } +def method(func: Callable) -> Callable: + """Decorator to define methods within Modal classes""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +@app.cls() +class DataProcessor: + def __init__(self, model_path: str): + # Constructor runs during instance creation + self.model = load_model(model_path) + self.cache = {} + + @modal.method() + def process_single(self, data: str) -> str: + # Method can access instance state + if data in self.cache: + return self.cache[data] + + result = self.model.predict(data) + self.cache[data] = result + return result + + @modal.method() + def process_batch(self, data_list: list[str]) -> list[str]: + # Another method sharing the same instance state + return [self.process_single(data) for data in data_list] + + @modal.method() + def get_cache_size(self) -> int: + return len(self.cache) + +# Usage +@app.local_entrypoint() +def main(): + processor = DataProcessor("path/to/model") + + # Call methods on the remote instance + result1 = processor.process_single.remote("input1") + result2 = processor.process_batch.remote(["input2", "input3"]) + cache_size = processor.get_cache_size.remote() + + print(f"Results: {result1}, {result2}") + print(f"Cache size: {cache_size}") +``` + +### Parameter Helper + +Helper function for defining class initialization parameters with validation and default values, similar to dataclass fields. + +```python { .api } +def parameter(*, default: Any = _no_default, init: bool = True) -> Any: + """Define class initialization parameters with options""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +@app.cls() +class ConfigurableService: + # Parameters with type annotations and defaults + model_name: str = modal.parameter() + batch_size: int = modal.parameter(default=32) + temperature: float = modal.parameter(default=0.7) + debug_mode: bool = modal.parameter(default=False) + + # Internal field not used in constructor + _internal_cache: dict = modal.parameter(init=False) + + def __post_init__(self): + # Initialize internal state after parameter injection + self._internal_cache = {} + print(f"Service initialized with model={self.model_name}, batch_size={self.batch_size}") + + @modal.method() + def configure_service(self): + # Use parameters in methods + if self.debug_mode: + print(f"Debug: Processing with temperature={self.temperature}") + + return { + "model": self.model_name, + "batch_size": self.batch_size, + "temperature": self.temperature + } + +# Usage with different configurations +@app.local_entrypoint() +def main(): + # Create instances with different parameters + service1 = ConfigurableService("gpt-4", batch_size=64, debug_mode=True) + service2 = ConfigurableService("claude-3", temperature=0.5) + + config1 = service1.configure_service.remote() + config2 = service2.configure_service.remote() + + print("Service 1 config:", config1) + print("Service 2 config:", config2) +``` + +### Lifecycle Decorators + +Decorators for defining class lifecycle methods that run during container startup and shutdown. + +```python { .api } +def enter(func: Callable) -> Callable: + """Decorator for class enter lifecycle method (runs on container startup)""" + +def exit(func: Callable) -> Callable: + """Decorator for class exit lifecycle method (runs on container shutdown)""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +@app.cls() +class DatabaseService: + def __init__(self, connection_string: str): + self.connection_string = connection_string + self.connection = None + self.cache = None + + @modal.enter() + def setup_connections(self): + """Run once when container starts""" + print("Setting up database connection...") + self.connection = create_database_connection(self.connection_string) + self.cache = initialize_cache() + print("Database service ready!") + + @modal.exit() + def cleanup_connections(self): + """Run once when container shuts down""" + print("Cleaning up database connections...") + if self.connection: + self.connection.close() + if self.cache: + self.cache.clear() + print("Cleanup complete!") + + @modal.method() + def query_data(self, sql: str) -> list[dict]: + # Connection is already established from enter() + cursor = self.connection.cursor() + cursor.execute(sql) + return cursor.fetchall() + + @modal.method() + def cached_query(self, sql: str) -> list[dict]: + # Use cache initialized in enter() + if sql in self.cache: + return self.cache[sql] + + result = self.query_data(sql) + self.cache[sql] = result + return result + +# Usage +@app.local_entrypoint() +def main(): + db_service = DatabaseService("postgresql://user:pass@host:5432/db") + + # First call triggers enter() lifecycle + results = db_service.query_data.remote("SELECT * FROM users LIMIT 10") + + # Subsequent calls reuse the established connection + cached_results = db_service.cached_query.remote("SELECT COUNT(*) FROM users") + + print("Query results:", results) + print("Cached results:", cached_results) + + # Container shutdown triggers exit() lifecycle automatically +``` + +### Execution Control Decorators + +Decorators for controlling how functions execute, including batching and concurrency patterns. + +```python { .api } +def batched(max_batch_size: int = 10) -> Callable: + """Decorator to enable batched function calls for improved throughput""" + +def concurrent(func: Callable) -> Callable: + """Decorator to enable concurrent function execution""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +@app.function() +@modal.batched(max_batch_size=50) +def process_items_batched(items: list[str]) -> list[str]: + """Process multiple items in a single function call""" + print(f"Processing batch of {len(items)} items") + + # Expensive setup that benefits from batching + model = load_expensive_model() + + # Process all items in the batch + results = [] + for item in items: + result = model.process(item) + results.append(result) + + return results + +@app.function() +@modal.concurrent +def process_item_concurrent(item: str) -> str: + """Process items with concurrent execution""" + # Each call can run concurrently with others + return expensive_processing(item) + +@app.local_entrypoint() +def main(): + # Batched processing - items are automatically grouped + items = [f"item_{i}" for i in range(100)] + + # These calls will be automatically batched up to max_batch_size + batch_results = [] + for item in items: + result = process_items_batched.remote([item]) # Each call adds to batch + batch_results.append(result) + + print(f"Batched processing completed: {len(batch_results)} results") + + # Concurrent processing - items run in parallel + concurrent_futures = [] + for item in items[:10]: # Process first 10 concurrently + future = process_item_concurrent.spawn(item) + concurrent_futures.append(future) + + # Collect concurrent results + concurrent_results = [future.get() for future in concurrent_futures] + print(f"Concurrent processing completed: {len(concurrent_results)} results") +``` + +## Advanced Patterns + +### Stateful Service with Lifecycle Management + +```python +import modal + +app = modal.App() + +@app.cls() +class MLInferenceService: + model_name: str = modal.parameter() + cache_size: int = modal.parameter(default=1000) + + @modal.enter() + def load_model(self): + """Load model and initialize cache on container start""" + print(f"Loading model: {self.model_name}") + self.model = download_and_load_model(self.model_name) + self.prediction_cache = LRUCache(maxsize=self.cache_size) + self.stats = {"requests": 0, "cache_hits": 0} + print("Model loaded and ready for inference") + + @modal.exit() + def save_stats(self): + """Save statistics before container shutdown""" + print(f"Final stats: {self.stats}") + save_stats_to_database(self.stats) + + @modal.method() + @modal.batched(max_batch_size=32) + def predict_batch(self, inputs: list[str]) -> list[dict]: + """Batched prediction with caching""" + results = [] + uncached_inputs = [] + uncached_indices = [] + + # Check cache for each input + for i, inp in enumerate(inputs): + if inp in self.prediction_cache: + results.append(self.prediction_cache[inp]) + self.stats["cache_hits"] += 1 + else: + results.append(None) # Placeholder + uncached_inputs.append(inp) + uncached_indices.append(i) + + # Batch process uncached inputs + if uncached_inputs: + batch_predictions = self.model.predict(uncached_inputs) + for idx, prediction in zip(uncached_indices, batch_predictions): + self.prediction_cache[inputs[idx]] = prediction + results[idx] = prediction + + self.stats["requests"] += len(inputs) + return results + + @modal.method() + def get_stats(self) -> dict: + """Get current service statistics""" + return self.stats.copy() + +# Usage +@app.local_entrypoint() +def main(): + # Create service instance + ml_service = MLInferenceService(model_name="bert-base-uncased", cache_size=500) + + # Make predictions (automatically batched) + test_inputs = [f"test sentence {i}" for i in range(100)] + predictions = ml_service.predict_batch.remote(test_inputs) + + # Check service statistics + stats = ml_service.get_stats.remote() + print(f"Service stats: {stats}") + + # Make some repeated predictions to test caching + repeat_predictions = ml_service.predict_batch.remote(test_inputs[:10]) + final_stats = ml_service.get_stats.remote() + print(f"Final stats with cache hits: {final_stats}") +``` + +### Concurrent Task Processing with Shared State + +```python +import modal + +app = modal.App() + +@app.cls() +class TaskProcessor: + max_workers: int = modal.parameter(default=10) + + @modal.enter() + def setup_processor(self): + """Initialize shared resources""" + self.task_queue = initialize_task_queue() + self.result_store = initialize_result_store() + self.worker_stats = {} + + @modal.method() + @modal.concurrent + def process_task_concurrent(self, task_id: str, worker_id: str) -> dict: + """Process individual tasks concurrently""" + # Track worker statistics + if worker_id not in self.worker_stats: + self.worker_stats[worker_id] = {"processed": 0, "errors": 0} + + try: + # Process the task + task_data = self.task_queue.get_task(task_id) + result = expensive_task_processing(task_data) + + # Store result + self.result_store.put(task_id, result) + self.worker_stats[worker_id]["processed"] += 1 + + return {"status": "success", "task_id": task_id, "worker": worker_id} + + except Exception as e: + self.worker_stats[worker_id]["errors"] += 1 + return {"status": "error", "task_id": task_id, "error": str(e)} + + @modal.method() + def get_worker_stats(self) -> dict: + """Get statistics for all workers""" + return self.worker_stats.copy() + +@app.local_entrypoint() +def main(): + processor = TaskProcessor(max_workers=20) + + # Process many tasks concurrently + task_ids = [f"task_{i}" for i in range(100)] + futures = [] + + for i, task_id in enumerate(task_ids): + worker_id = f"worker_{i % 20}" # Distribute across workers + future = processor.process_task_concurrent.spawn(task_id, worker_id) + futures.append(future) + + # Collect results + results = [future.get() for future in futures] + + # Check worker statistics + stats = processor.get_worker_stats.remote() + print(f"Worker statistics: {stats}") + + # Analyze results + successful = sum(1 for r in results if r["status"] == "success") + errors = sum(1 for r in results if r["status"] == "error") + print(f"Processed {successful} tasks successfully, {errors} errors") +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/index.md b/.tessl/tiles/tessl/pypi-modal/docs/index.md new file mode 100644 index 000000000..9ae30e179 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/index.md @@ -0,0 +1,346 @@ +# Modal + +Modal is a Python client library for Modal, a serverless cloud computing platform that enables developers to run Python code in the cloud with on-demand access to compute resources. The library provides a comprehensive toolkit for building, deploying, and managing serverless applications including functions, classes, containers, volumes, secrets, and file systems. It features a rich API for defining cloud resources declaratively, supports parallel execution and distributed computing patterns, includes built-in monitoring and logging capabilities, and provides seamless integration with popular Python frameworks and libraries. + +## Package Information + +- **Package Name**: modal +- **Language**: Python +- **Installation**: `pip install modal` +- **Python Requirements**: >= 3.9, < 3.14 + +## Core Imports + +```python +import modal +``` + +Common pattern for accessing main classes: + +```python +from modal import App, Image, Secret, Volume +``` + +Alternative import all public APIs: + +```python +from modal import * +``` + +## Basic Usage + +```python +import modal + +# Define an application +app = modal.App("my-app") + +# Define a custom container image with dependencies +image = modal.Image.debian_slim().pip_install("requests", "beautifulsoup4") + +# Define a serverless function +@app.function(image=image) +def scrape_url(url: str) -> str: + import requests + from bs4 import BeautifulSoup + + response = requests.get(url) + soup = BeautifulSoup(response.content, 'html.parser') + return soup.get_text() + +# Local entrypoint to run the function +@app.local_entrypoint() +def main(): + result = scrape_url.remote("https://example.com") + print(result) + +# Deploy the app +if __name__ == "__main__": + app.run() +``` + +## Architecture + +Modal follows a declarative resource model where applications are composed of: + +- **App**: Main container that groups functions, classes, and resources +- **Compute Resources**: Functions and Classes that execute in the cloud +- **Infrastructure**: Storage (Volumes, NetworkFileSystem), networking (Proxy, Tunnel), and security (Secret) +- **Images**: Container definitions that specify the runtime environment +- **Scheduling**: Cron jobs and periodic tasks for automated execution + +The library uses the synchronicity pattern to provide both synchronous and asynchronous APIs from a single codebase, enabling flexible usage patterns while maintaining type safety. + +## Capabilities + +### Core Application & Client + +Primary interfaces for defining applications and managing authentication with Modal's cloud platform. + +```python { .api } +class App: + def __init__(self, name: str): ... + def function(self, **kwargs): ... # Decorator + def cls(self, **kwargs): ... # Decorator + def local_entrypoint(self): ... # Decorator + def deploy(self): ... + def run(self): ... + +class Client: + @classmethod + def from_env(cls): ... + def lookup(self, label: str): ... +``` + +[Core Application & Client](./core-application-client.md) + +### Compute Resources + +Functions and classes that execute serverless workloads in Modal's cloud infrastructure. + +```python { .api } +class Function: + def remote(self, *args, **kwargs): ... + def local(self, *args, **kwargs): ... + def map(self, inputs): ... + def spawn(self, *args, **kwargs): ... + +class FunctionCall: + def get(self): ... + def cancel(self): ... + +class Cls: + @classmethod + def from_name(cls, label: str): ... + def lookup(self, name: str): ... + +class Sandbox: + @classmethod + def create(cls, **kwargs): ... + def exec(self, command: str): ... + def terminate(self): ... +``` + +[Compute Resources](./compute-resources.md) + +### Container Images + +Container image definitions that specify the runtime environment for functions and classes. + +```python { .api } +class Image: + @classmethod + def debian_slim(cls, python_version: str = None): ... + @classmethod + def from_registry(cls, tag: str): ... + def pip_install(self, *packages: str): ... + def run_commands(self, *commands: str): ... + def copy_local_file(self, local_path: str, remote_path: str): ... +``` + +[Container Images](./container-images.md) + +### Storage & Data + +Persistent storage solutions including volumes, network file systems, key-value stores, and cloud bucket mounts. + +```python { .api } +class Volume: + @classmethod + def from_name(cls, label: str): ... + @classmethod + def persist(cls, label: str): ... + +class NetworkFileSystem: + @classmethod + def from_name(cls, label: str): ... + @classmethod + def persist(cls, label: str): ... + +class Dict: + @classmethod + def from_name(cls, label: str): ... + def get(self, key: str): ... + def put(self, key: str, value): ... + def pop(self, key: str): ... + +class Queue: + @classmethod + def from_name(cls, label: str): ... + def put(self, item): ... + def get(self): ... +``` + +[Storage & Data](./storage-data.md) + +### Infrastructure Services + +Networking, security, and infrastructure services for cloud applications. + +```python { .api } +class Secret: + @classmethod + def from_name(cls, label: str): ... + @classmethod + def from_dict(cls, mapping: dict): ... + +class Proxy: + @classmethod + def from_name(cls, label: str): ... + +class Tunnel: + @classmethod + def create(cls, **kwargs): ... + +class SchedulerPlacement: + @classmethod + def zone(cls, zone: str): ... +``` + +[Infrastructure Services](./infrastructure-services.md) + +### Function Decorators & Helpers + +Decorators and helper functions for enhancing function behavior and defining lifecycle methods. + +```python { .api } +def method(func): ... +def parameter(name: str, default=None): ... +def enter(func): ... +def exit(func): ... +def batched(max_batch_size: int): ... +def concurrent(func): ... +``` + +[Function Decorators & Helpers](./function-decorators-helpers.md) + +### Web & API Integration + +Web application serving capabilities including ASGI, WSGI, and HTTP endpoint support. + +```python { .api } +def asgi_app(func): ... +def wsgi_app(func): ... +def web_endpoint(func): ... +def fastapi_endpoint(func): ... +def web_server(func): ... +``` + +[Web & API Integration](./web-api-integration.md) + +### Scheduling & Reliability + +Task scheduling and retry policies for automated and resilient execution. + +```python { .api } +class Cron: + def __init__(self, cron_string: str): ... + +class Period: + @classmethod + def days(cls, n: int): ... + @classmethod + def hours(cls, n: int): ... + @classmethod + def minutes(cls, n: int): ... + @classmethod + def seconds(cls, n: int): ... + +class Retries: + def __init__( + self, + max_retries: int = 3, + backoff: float = 2.0, + initial_delay: float = 1.0 + ): ... +``` + +[Scheduling & Reliability](./scheduling-reliability.md) + +### Runtime Utilities + +Utilities for runtime context, debugging, and execution control within Modal functions. + +```python { .api } +def current_function_call_id() -> str: ... +def current_input_id() -> str: ... +def is_local() -> bool: ... +def interact(): ... +def enable_output(): ... +def forward(**kwargs): ... +``` + +[Runtime Utilities](./runtime-utilities.md) + +### Utility Classes + +General utility classes for error handling, file pattern matching, and package information. + +```python { .api } +__version__: str # Package version + +class Error(Exception): ... + +class FilePatternMatcher: + def __init__(self, patterns: list): ... + def matches(self, path: str) -> bool: ... +``` + +[Utility Classes](./utility-classes.md) + +## Common Patterns + +### Function with Custom Environment + +```python +import modal + +app = modal.App() + +@app.function( + image=modal.Image.debian_slim().pip_install("numpy", "pandas"), + secrets=[modal.Secret.from_name("my-secret")], + volumes={"/data": modal.Volume.from_name("my-volume")} +) +def process_data(filename: str): + import pandas as pd + df = pd.read_csv(f"/data/{filename}") + return df.describe().to_dict() +``` + +### Scheduled Function + +```python +from modal import App, Cron + +app = App() + +@app.function(schedule=Cron("0 0 * * *")) # Daily at midnight +def daily_report(): + # Generate and send daily report + print("Running daily report...") +``` + +### Class with Lifecycle Methods + +```python +from modal import App, enter, exit, method + +app = App() + +@app.cls() +class MyService: + @enter() + def setup(self): + # Initialize resources + self.client = create_client() + + @exit() + def cleanup(self): + # Clean up resources + self.client.close() + + @method() + def process(self, data): + return self.client.process(data) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md b/.tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md new file mode 100644 index 000000000..90bbf2f66 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md @@ -0,0 +1,389 @@ +# Infrastructure Services + +Modal provides essential infrastructure services for networking, security, and resource management. These services enable secure communication, secret management, network connectivity, and fine-grained control over where functions execute. + +## Capabilities + +### Secret - Secure Environment Variable Management + +Secure storage and injection of environment variables, API keys, database credentials, and other sensitive data into Modal functions. + +```python { .api } +class Secret: + @classmethod + def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Secret": + """Load a Secret by its unique name""" + + @classmethod + def from_dict( + cls, + env_dict: dict[str, Optional[str]], + *, + name: Optional[str] = None + ) -> "Secret": + """Create a Secret from a dictionary of environment variables""" + + @classmethod + def from_dotenv(cls, path: Optional[str] = None) -> "Secret": + """Create a Secret from a .env file""" + +class SecretInfo: + """Information about a Secret object""" + name: Optional[str] + created_at: datetime + created_by: Optional[str] +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +# Create secret from dictionary +db_secret = modal.Secret.from_dict({ + "DATABASE_URL": "postgresql://user:pass@host:5432/db", + "DATABASE_PASSWORD": "secure_password", + "API_KEY": "your_api_key_here" +}) + +# Load existing secret by name +api_secret = modal.Secret.from_name("openai-api-key") + +@app.function(secrets=[db_secret, api_secret]) +def database_operation(): + import os + + # Access secret values as environment variables + db_url = os.environ["DATABASE_URL"] + api_key = os.environ["API_KEY"] + + # Use secrets in your code + connection = create_database_connection(db_url) + client = APIClient(api_key) + + return process_data(connection, client) + +# Create secret from .env file +@app.local_entrypoint() +def main(): + # Load secrets from local .env file + local_secrets = modal.Secret.from_dotenv(".env") + + # Deploy function with secrets + result = database_operation.remote() + print(result) +``` + +### Proxy - Static Outbound IP Address + +Proxy objects provide Modal containers with static outbound IP addresses for connecting to services that require IP whitelisting. + +```python { .api } +class Proxy: + @classmethod + def from_name(cls, name: str, *, environment_name: Optional[str] = None) -> "Proxy": + """Reference a Proxy by its name (must be provisioned via Dashboard)""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +# Reference proxy created in Modal Dashboard +proxy = modal.Proxy.from_name("production-proxy") + +@app.function(proxy=proxy) +def connect_to_whitelisted_service(): + import requests + + # This request will come from the proxy's static IP + response = requests.get("https://api.partner.com/data") + + # The partner service sees requests from the proxy's static IP + # which can be added to their IP whitelist + return response.json() + +# Multiple functions can share the same proxy +@app.function(proxy=proxy) +def another_whitelisted_operation(): + # Also uses the same static IP + return make_api_call() +``` + +### Tunnel - Network Tunnel for Remote Services + +Network tunnels enable secure connections between Modal functions and remote services, useful for development and accessing services behind firewalls. + +```python { .api } +class Tunnel: + @classmethod + def create( + cls, + *, + host: str = "0.0.0.0", + port: int = 8000, + timeout: Optional[int] = None + ) -> "Tunnel": + """Create a network tunnel""" + +def forward(port: int, *, host: str = "localhost") -> str: + """Forward network traffic through tunnel (utility function)""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +@app.function() +def create_development_tunnel(): + # Create tunnel for development access + tunnel = modal.Tunnel.create(port=8000) + + # Start a simple web server + from http.server import HTTPServer, SimpleHTTPRequestHandler + + server = HTTPServer(("0.0.0.0", 8000), SimpleHTTPRequestHandler) + print(f"Server accessible via tunnel: {tunnel.url}") + + # Server runs and is accessible through tunnel + server.serve_forever() + +@app.function() +def connect_through_tunnel(): + # Forward local service through tunnel + tunnel_url = modal.forward(5432, host="database.internal") + + # Connect to database through tunnel + connection = create_connection(tunnel_url) + return query_database(connection) +``` + +### SchedulerPlacement - Control Function Placement + +Control where functions are scheduled to run, enabling optimization for latency, compliance, or resource availability. + +```python { .api } +class SchedulerPlacement: + @classmethod + def zone(cls, zone: str) -> "SchedulerPlacement": + """Set preferred zone/region for function execution""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +# Place functions in specific zones for latency optimization +us_east_placement = modal.SchedulerPlacement.zone("us-east-1") +eu_placement = modal.SchedulerPlacement.zone("eu-west-1") + +@app.function(scheduler_placement=us_east_placement) +def process_us_data(): + # This function will preferentially run in us-east-1 + # for low latency to US-based services + return fetch_and_process_us_data() + +@app.function(scheduler_placement=eu_placement) +def process_eu_data(): + # This function will preferentially run in eu-west-1 + # for GDPR compliance and low latency to EU services + return fetch_and_process_eu_data() + +@app.function() +def coordinator(): + # Start processing in appropriate regions + us_result = process_us_data.spawn() + eu_result = process_eu_data.spawn() + + # Collect results + return { + "us": us_result.get(), + "eu": eu_result.get() + } +``` + +## Infrastructure Patterns + +### Multi-Environment Secret Management + +```python +import modal + +app = modal.App() + +# Different secrets for different environments +dev_secrets = modal.Secret.from_dict({ + "DATABASE_URL": "postgresql://localhost:5432/dev_db", + "API_ENDPOINT": "https://api-dev.example.com", + "DEBUG": "true" +}) + +prod_secrets = modal.Secret.from_name("production-secrets") + +# Environment-specific function deployment +@app.function(secrets=[dev_secrets]) +def dev_function(): + return process_with_dev_config() + +@app.function(secrets=[prod_secrets]) +def prod_function(): + return process_with_prod_config() +``` + +### Secure Multi-Service Integration + +```python +import modal + +app = modal.App() + +# Multiple secrets for different services +database_secret = modal.Secret.from_name("postgres-credentials") +api_secret = modal.Secret.from_name("external-api-keys") +cloud_secret = modal.Secret.from_name("aws-credentials") + +# Static IP for whitelisted services +proxy = modal.Proxy.from_name("main-proxy") + +@app.function( + secrets=[database_secret, api_secret, cloud_secret], + proxy=proxy +) +def integrated_data_pipeline(): + import os + import boto3 + import psycopg2 + import requests + + # Database connection using secret + db_conn = psycopg2.connect(os.environ["DATABASE_URL"]) + + # AWS client using secret + s3_client = boto3.client( + 's3', + aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"], + aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"] + ) + + # API call using secret and proxy (for static IP) + headers = {"Authorization": f"Bearer {os.environ['API_TOKEN']}"} + response = requests.get("https://api.partner.com/data", headers=headers) + + # Process data across all services + data = response.json() + processed = transform_data(data) + + # Store in database + store_in_database(db_conn, processed) + + # Backup to S3 + backup_to_s3(s3_client, processed) + + return {"status": "success", "records": len(processed)} +``` + +### Geographically Distributed Processing + +```python +import modal + +app = modal.App() + +# Regional placement for compliance and latency +us_placement = modal.SchedulerPlacement.zone("us-east-1") +eu_placement = modal.SchedulerPlacement.zone("eu-west-1") +asia_placement = modal.SchedulerPlacement.zone("ap-southeast-1") + +# Regional secrets for compliance +us_secrets = modal.Secret.from_name("us-compliance-keys") +eu_secrets = modal.Secret.from_name("eu-gdpr-keys") +asia_secrets = modal.Secret.from_name("asia-keys") + +@app.function( + scheduler_placement=us_placement, + secrets=[us_secrets] +) +def process_us_customer_data(customer_ids: list[str]): + # Process US customer data in US region for compliance + return [process_customer(id) for id in customer_ids] + +@app.function( + scheduler_placement=eu_placement, + secrets=[eu_secrets] +) +def process_eu_customer_data(customer_ids: list[str]): + # Process EU customer data in EU region for GDPR compliance + return [process_customer_gdpr(id) for id in customer_ids] + +@app.function() +def global_customer_processing(): + # Route customers to appropriate regional processors + us_customers = get_us_customers() + eu_customers = get_eu_customers() + + # Process in appropriate regions simultaneously + us_task = process_us_customer_data.spawn(us_customers) + eu_task = process_eu_customer_data.spawn(eu_customers) + + # Collect results + us_results = us_task.get() + eu_results = eu_task.get() + + return combine_regional_results(us_results, eu_results) +``` + +### Development to Production Pipeline + +```python +import modal + +# Development setup +dev_app = modal.App("data-pipeline-dev") +dev_secrets = modal.Secret.from_dict({ + "DATABASE_URL": "postgresql://localhost:5432/dev", + "API_KEY": "dev_key_123", + "S3_BUCKET": "dev-data-bucket" +}) + +# Production setup +prod_app = modal.App("data-pipeline-prod") +prod_secrets = modal.Secret.from_name("production-secrets") +prod_proxy = modal.Proxy.from_name("production-proxy") + +# Shared function logic with environment-specific configuration +def create_pipeline_function(app, secrets, proxy=None): + @app.function(secrets=secrets, proxy=proxy) + def data_pipeline(): + import os + + # Environment variables automatically injected based on secrets + db_url = os.environ["DATABASE_URL"] + api_key = os.environ["API_KEY"] + bucket = os.environ["S3_BUCKET"] + + # Same processing logic, different configurations + return process_data(db_url, api_key, bucket) + + return data_pipeline + +# Create environment-specific functions +dev_pipeline = create_pipeline_function(dev_app, [dev_secrets]) +prod_pipeline = create_pipeline_function(prod_app, [prod_secrets], prod_proxy) + +# Deploy based on environment +if os.getenv("ENVIRONMENT") == "production": + prod_pipeline.remote() +else: + dev_pipeline.remote() +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md b/.tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md new file mode 100644 index 000000000..8317a9bb2 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md @@ -0,0 +1,626 @@ +# Runtime Utilities + +Modal provides essential runtime utilities for execution context awareness, debugging, output control, and network tunneling within Modal functions. These utilities enable functions to understand their execution environment and provide enhanced debugging and development capabilities. + +## Capabilities + +### Execution Context Functions + +Functions for understanding the current execution context and accessing runtime information. + +```python { .api } +def current_function_call_id() -> Optional[str]: + """Get the ID of the currently executing function call""" + +def current_input_id() -> Optional[str]: + """Get the ID of the current function input""" + +def is_local() -> bool: + """Check if code is running locally vs in Modal cloud""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("context-aware") + +@app.function() +def context_aware_function(data: str): + """Function that uses runtime context information""" + + # Check execution environment + if modal.is_local(): + print("Running locally - using development configuration") + debug_mode = True + log_level = "DEBUG" + else: + print("Running in Modal cloud - using production configuration") + debug_mode = False + log_level = "INFO" + + # Get runtime identifiers + call_id = modal.current_function_call_id() + input_id = modal.current_input_id() + + print(f"Function call ID: {call_id}") + print(f"Input ID: {input_id}") + + # Use context for logging and tracing + log_entry = { + "timestamp": time.time(), + "function_call_id": call_id, + "input_id": input_id, + "environment": "local" if modal.is_local() else "cloud", + "data": data, + "debug_mode": debug_mode + } + + # Process data with context-aware behavior + if debug_mode: + result = process_with_detailed_logging(data, log_entry) + else: + result = process_efficiently(data, log_entry) + + return { + "result": result, + "call_id": call_id, + "input_id": input_id, + "environment": "local" if modal.is_local() else "cloud" + } + +@app.local_entrypoint() +def main(): + # Test locally + local_result = context_aware_function("local_test_data") + print("Local result:", local_result) + + # Test in cloud + cloud_result = context_aware_function.remote("cloud_test_data") + print("Cloud result:", cloud_result) +``` + +### Interactive Debugging + +Function for enabling interactive debugging sessions within Modal containers. + +```python { .api } +def interact() -> None: + """Start an interactive debugging session (only works in Modal containers)""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("debugging") + +@app.function() +def debug_function(data: dict): + """Function with interactive debugging capabilities""" + + print("Starting data processing...") + + # Process first part + intermediate_result = preprocess_data(data) + + # Interactive debugging point + if should_debug(data): + print("Entering interactive debugging mode...") + print("Available variables: data, intermediate_result") + print("Type 'continue' or Ctrl+D to exit debugging") + + # This opens an interactive Python shell inside the container + modal.interact() + + # Continue processing after debugging + final_result = postprocess_data(intermediate_result) + + return final_result + +def should_debug(data: dict) -> bool: + """Determine if debugging should be enabled""" + # Enable debugging for specific conditions + return ( + data.get("debug_flag", False) or + data.get("complexity_score", 0) > 0.8 or + "error_prone_input" in str(data) + ) + +@app.local_entrypoint() +def main(): + # Normal execution + result1 = debug_function.remote({"value": 42}) + print("Normal result:", result1) + + # Execution with debugging + result2 = debug_function.remote({ + "value": 42, + "debug_flag": True, + "complex_data": [1, 2, 3, 4, 5] + }) + print("Debug result:", result2) +``` + +### Output Control + +Function for controlling output display and progress indicators during Modal function execution. + +```python { .api } +def enable_output(show_progress: bool = True) -> None: + """Enable output streaming for functions (context manager)""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("output-control") + +@app.function() +def processing_function(items: list[str]) -> list[str]: + """Function that processes items with output""" + results = [] + + print("Starting batch processing...") + + for i, item in enumerate(items): + # Print progress + print(f"Processing item {i+1}/{len(items)}: {item}") + + # Simulate processing + result = expensive_operation(item) + results.append(result) + + # Periodic status updates + if (i + 1) % 10 == 0: + print(f"Completed {i+1} items, {len(items) - i - 1} remaining") + + print("Batch processing completed!") + return results + +@app.function() +def map_processing_function(item: str) -> str: + """Function for use with map() that shows progress""" + print(f"Processing: {item}") + result = complex_processing(item) + print(f"Completed: {item} -> {result}") + return result + +@app.local_entrypoint() +def main(): + # Enable output to see function logs and progress + with modal.enable_output(show_progress=True): + + # Single function with output + items = [f"item_{i}" for i in range(50)] + batch_result = processing_function.remote(items) + print(f"Batch processing completed: {len(batch_result)} results") + + # Map with progress tracking + map_items = [f"map_item_{i}" for i in range(20)] + map_results = list(map_processing_function.map(map_items)) + print(f"Map processing completed: {len(map_results)} results") + +# Alternative: Enable output for specific operations +@app.local_entrypoint() +def selective_output(): + """Enable output only for specific operations""" + + # Normal execution (no output) + quiet_result = processing_function.remote([f"quiet_{i}" for i in range(5)]) + + # With output enabled + with modal.enable_output(): + verbose_result = processing_function.remote([f"verbose_{i}" for i in range(5)]) + + print("Selective output demo completed") +``` + +### Network Tunneling + +Function for forwarding network traffic through tunnels, enabling secure connections to remote services. + +```python { .api } +def forward(port: int, *, host: str = "localhost") -> str: + """Forward network traffic through tunnel""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("network-tunneling") + +@app.function() +def tunnel_database_connection(): + """Connect to database through network tunnel""" + + # Forward database port through tunnel + tunnel_url = modal.forward(5432, host="internal-database.company.com") + + print(f"Database tunnel established: {tunnel_url}") + + # Connect to database through tunnel + import psycopg2 + connection = psycopg2.connect( + host=tunnel_url.split("://")[1].split(":")[0], + port=int(tunnel_url.split(":")[-1]), + database="production_db", + user="app_user", + password=os.environ["DB_PASSWORD"] + ) + + # Use the connection + cursor = connection.cursor() + cursor.execute("SELECT COUNT(*) FROM users") + count = cursor.fetchone()[0] + + connection.close() + return {"user_count": count, "tunnel_url": tunnel_url} + +@app.function() +def tunnel_api_access(): + """Access internal API through tunnel""" + + # Forward API port + api_tunnel = modal.forward(8080, host="internal-api.company.com") + + print(f"API tunnel established: {api_tunnel}") + + # Make requests through tunnel + import requests + response = requests.get(f"{api_tunnel}/api/v1/data") + + return { + "api_response": response.json(), + "status_code": response.status_code, + "tunnel_url": api_tunnel + } + +@app.function() +def multi_service_tunneling(): + """Connect to multiple services through tunnels""" + + # Set up multiple tunnels + database_tunnel = modal.forward(5432, host="db.internal") + redis_tunnel = modal.forward(6379, host="redis.internal") + elasticsearch_tunnel = modal.forward(9200, host="es.internal") + + services = { + "database": database_tunnel, + "redis": redis_tunnel, + "elasticsearch": elasticsearch_tunnel + } + + print("All tunnels established:") + for service, url in services.items(): + print(f" {service}: {url}") + + # Use services through tunnels + results = {} + + # Database query + db_result = query_database_through_tunnel(database_tunnel) + results["database"] = db_result + + # Redis operation + redis_result = access_redis_through_tunnel(redis_tunnel) + results["redis"] = redis_result + + # Elasticsearch search + es_result = search_elasticsearch_through_tunnel(elasticsearch_tunnel) + results["elasticsearch"] = es_result + + return { + "results": results, + "tunnels": services + } + +def query_database_through_tunnel(tunnel_url: str) -> dict: + """Helper function to query database through tunnel""" + # Extract host and port from tunnel URL + host = tunnel_url.split("://")[1].split(":")[0] + port = int(tunnel_url.split(":")[-1]) + + # Connect and query + connection = create_db_connection(host, port) + cursor = connection.cursor() + cursor.execute("SELECT version()") + version = cursor.fetchone()[0] + connection.close() + + return {"version": version, "status": "connected"} + +def access_redis_through_tunnel(tunnel_url: str) -> dict: + """Helper function to access Redis through tunnel""" + import redis + + host = tunnel_url.split("://")[1].split(":")[0] + port = int(tunnel_url.split(":")[-1]) + + client = redis.Redis(host=host, port=port) + client.set("tunnel_test", "success") + value = client.get("tunnel_test").decode() + + return {"test_value": value, "status": "connected"} + +@app.local_entrypoint() +def main(): + # Test database tunnel + db_result = tunnel_database_connection.remote() + print("Database tunnel result:", db_result) + + # Test API tunnel + api_result = tunnel_api_access.remote() + print("API tunnel result:", api_result) + + # Test multiple tunnels + multi_result = multi_service_tunneling.remote() + print("Multi-service tunnel result:", multi_result) +``` + +## Advanced Runtime Patterns + +### Context-Aware Error Handling + +```python +import modal + +app = modal.App("context-error-handling") + +@app.function() +def robust_function(data: dict): + """Function with context-aware error handling""" + + call_id = modal.current_function_call_id() + input_id = modal.current_input_id() + is_local_env = modal.is_local() + + error_context = { + "call_id": call_id, + "input_id": input_id, + "environment": "local" if is_local_env else "cloud", + "timestamp": time.time() + } + + try: + result = risky_operation(data) + return {"success": True, "result": result, "context": error_context} + + except Exception as e: + # Enhanced error information with runtime context + error_info = { + "error": str(e), + "error_type": type(e).__name__, + "context": error_context, + "data_summary": { + "size": len(str(data)), + "keys": list(data.keys()) if isinstance(data, dict) else None + } + } + + # Different handling based on environment + if is_local_env: + print("Local error - entering debug mode") + print(f"Error context: {error_info}") + modal.interact() # Interactive debugging in local mode + else: + print("Cloud error - logging and continuing") + log_error_to_monitoring_system(error_info) + + return {"success": False, "error": error_info} +``` + +### Distributed Tracing with Context + +```python +import modal + +app = modal.App("distributed-tracing") + +# Shared tracing storage +trace_store = modal.Dict.persist("trace-data") + +@app.function() +def traced_function_a(data: str): + """First function in trace chain""" + + call_id = modal.current_function_call_id() + trace_id = f"trace_{int(time.time())}" + + # Start trace + trace_entry = { + "trace_id": trace_id, + "function": "traced_function_a", + "call_id": call_id, + "start_time": time.time(), + "input_data": data + } + + print(f"Starting trace {trace_id} in function A") + + # Process data + result_a = process_step_a(data) + + # Call next function with trace context + result_b = traced_function_b.remote(result_a, trace_id) + + # Complete trace entry + trace_entry.update({ + "end_time": time.time(), + "result": "completed", + "next_call": "traced_function_b" + }) + + # Store trace data + trace_store[f"{trace_id}_function_a"] = trace_entry + + return {"result": result_b, "trace_id": trace_id} + +@app.function() +def traced_function_b(data: str, trace_id: str): + """Second function in trace chain""" + + call_id = modal.current_function_call_id() + + trace_entry = { + "trace_id": trace_id, + "function": "traced_function_b", + "call_id": call_id, + "start_time": time.time(), + "input_data": data + } + + print(f"Continuing trace {trace_id} in function B") + + # Process data + result = process_step_b(data) + + # Complete trace + trace_entry.update({ + "end_time": time.time(), + "result": result + }) + + trace_store[f"{trace_id}_function_b"] = trace_entry + + return result + +@app.function() +def get_trace_summary(trace_id: str): + """Get complete trace summary""" + + # Collect all trace entries + trace_entries = {} + for key in trace_store.keys(): + if key.startswith(trace_id): + trace_entries[key] = trace_store.get(key) + + # Calculate trace metrics + start_times = [entry["start_time"] for entry in trace_entries.values()] + end_times = [entry["end_time"] for entry in trace_entries.values()] + + total_duration = max(end_times) - min(start_times) + + return { + "trace_id": trace_id, + "total_duration": total_duration, + "function_count": len(trace_entries), + "entries": trace_entries + } + +@app.local_entrypoint() +def main(): + with modal.enable_output(): + # Execute traced function chain + result = traced_function_a.remote("test_data") + + # Get trace summary + trace_summary = get_trace_summary.remote(result["trace_id"]) + print("Trace summary:", trace_summary) +``` + +### Development vs Production Runtime + +```python +import modal + +app = modal.App("environment-aware") + +@app.function() +def environment_aware_processing(data: dict): + """Function that adapts behavior based on runtime environment""" + + is_local_env = modal.is_local() + call_id = modal.current_function_call_id() + + # Environment-specific configuration + if is_local_env: + config = { + "timeout": 30, + "retries": 1, + "logging_level": "DEBUG", + "use_cache": False, + "external_apis": "staging" + } + print("Using development configuration") + else: + config = { + "timeout": 300, + "retries": 3, + "logging_level": "INFO", + "use_cache": True, + "external_apis": "production" + } + print("Using production configuration") + + print(f"Processing with config: {config}") + print(f"Call ID: {call_id}") + + # Adaptive processing based on environment + try: + if config["use_cache"]: + # Check cache first in production + cached_result = check_cache(data) + if cached_result: + return {"result": cached_result, "source": "cache", "config": config} + + # Process with environment-specific settings + result = process_with_config(data, config) + + if config["use_cache"]: + # Cache result in production + store_in_cache(data, result) + + return {"result": result, "source": "computed", "config": config} + + except Exception as e: + error_info = { + "error": str(e), + "environment": "local" if is_local_env else "production", + "call_id": call_id, + "config": config + } + + if is_local_env: + # More verbose error handling in development + print(f"Development error: {error_info}") + modal.interact() # Debug interactively + + return {"error": error_info, "success": False} + +def process_with_config(data: dict, config: dict): + """Process data with environment-specific configuration""" + + # Simulate processing with different timeouts + import time + processing_time = min(config["timeout"] / 10, 5) # Simulate work + time.sleep(processing_time) + + # Different API endpoints based on environment + api_base = f"https://api-{config['external_apis']}.example.com" + + return { + "processed_data": f"processed_{data}", + "api_used": api_base, + "processing_time": processing_time + } + +@app.local_entrypoint() +def main(): + test_data = {"input": "test_value", "timestamp": time.time()} + + with modal.enable_output(): + # This will use development config when run locally + local_result = environment_aware_processing(test_data) + print("Local result:", local_result) + + # This will use production config when run in Modal cloud + cloud_result = environment_aware_processing.remote(test_data) + print("Cloud result:", cloud_result) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md b/.tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md new file mode 100644 index 000000000..b40add20b --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md @@ -0,0 +1,456 @@ +# Scheduling & Reliability + +Modal provides comprehensive scheduling and reliability features for automated task execution and resilient function behavior. These capabilities enable building robust serverless applications with automatic retry policies and flexible scheduling patterns. + +## Capabilities + +### Cron Scheduling + +Schedule functions to run at specific times using Unix cron syntax, supporting timezone-aware scheduling for automated tasks. + +```python { .api } +class Cron: + def __init__(self, cron_string: str, timezone: str = "UTC") -> None: + """Create a cron schedule from cron expression string""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("scheduled-tasks") + +# Run every minute +@app.function(schedule=modal.Cron("* * * * *")) +def check_system_health(): + """Monitor system health every minute""" + health_status = check_all_services() + if not health_status["healthy"]: + send_alert(health_status["issues"]) + return health_status + +# Run daily at 4:05 AM UTC +@app.function(schedule=modal.Cron("5 4 * * *")) +def daily_backup(): + """Perform daily database backup""" + print("Starting daily backup...") + backup_result = create_database_backup() + upload_to_cloud_storage(backup_result) + cleanup_old_backups() + print("Daily backup completed") + +# Run every Thursday at 9 AM UTC +@app.function(schedule=modal.Cron("0 9 * * 4")) +def weekly_report(): + """Generate weekly analytics report""" + report_data = generate_weekly_analytics() + send_report_email(report_data) + store_report(report_data) + +# Run daily at 6 AM New York time (timezone-aware) +@app.function(schedule=modal.Cron("0 6 * * *", timezone="America/New_York")) +def business_hours_task(): + """Task that runs at business hours in New York""" + # Automatically adjusts for daylight saving time + process_business_data() + +# Complex cron expressions +@app.function(schedule=modal.Cron("15 2 * * 1-5")) # Weekdays at 2:15 AM +def weekday_maintenance(): + """Maintenance tasks during weekdays""" + perform_system_maintenance() + +@app.function(schedule=modal.Cron("0 12 1 * *")) # First day of month at noon +def monthly_billing(): + """Process monthly billing on first day of month""" + process_monthly_invoices() + send_billing_notifications() +``` + +### Period Scheduling + +Schedule functions to run at regular intervals using natural time periods, supporting precise timing for recurring tasks. + +```python { .api } +class Period: + def __init__( + self, + *, + years: int = 0, + months: int = 0, + weeks: int = 0, + days: int = 0, + hours: int = 0, + minutes: int = 0, + seconds: float = 0 + ) -> None: + """Create a periodic schedule with specified intervals""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("periodic-tasks") + +# Run every day +@app.function(schedule=modal.Period(days=1)) +def daily_data_sync(): + """Synchronize data daily""" + sync_external_apis() + update_local_cache() + print("Daily sync completed") + +# Run every 4 hours +@app.function(schedule=modal.Period(hours=4)) +def frequent_health_check(): + """Check system health every 4 hours""" + status = perform_detailed_health_check() + log_health_metrics(status) + +# Run every 15 minutes +@app.function(schedule=modal.Period(minutes=15)) +def cache_refresh(): + """Refresh application cache every 15 minutes""" + refresh_application_cache() + update_cache_metrics() + +# Run every 30 seconds +@app.function(schedule=modal.Period(seconds=30)) +def high_frequency_monitor(): + """High-frequency monitoring task""" + collect_realtime_metrics() + check_alert_conditions() + +# Complex periods +@app.function(schedule=modal.Period(weeks=2, days=3)) # Every 17 days +def bi_weekly_plus_task(): + """Task that runs every 2 weeks + 3 days""" + perform_special_analysis() + +@app.function(schedule=modal.Period(months=1)) # Monthly (same day each month) +def monthly_report(): + """Generate monthly report on same day each month""" + # Handles varying month lengths correctly + generate_monthly_analytics() + archive_old_data() + +# High precision timing +@app.function(schedule=modal.Period(seconds=3.14159)) # Every π seconds +def precise_timing_task(): + """Task with precise timing requirements""" + collect_high_precision_measurements() +``` + +### Retry Policies + +Configure automatic retry behavior for functions to handle transient failures and improve reliability. + +```python { .api } +class Retries: + def __init__( + self, + *, + max_retries: int, + backoff_coefficient: float = 2.0, + initial_delay: float = 1.0, + max_delay: float = 60.0 + ) -> None: + """Configure retry policy with exponential or fixed backoff""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("reliable-functions") + +# Simple retry configuration +@app.function(retries=4) # Retry up to 4 times with default settings +def simple_retry_task(): + """Task with basic retry policy""" + result = call_external_api() + return result + +# Fixed-interval retries +@app.function( + retries=modal.Retries( + max_retries=3, + backoff_coefficient=1.0, # Fixed delay + initial_delay=5.0, # 5 seconds between retries + ) +) +def fixed_retry_task(): + """Task with fixed 5-second delay between retries""" + try: + return process_critical_data() + except Exception as e: + print(f"Attempt failed: {e}") + raise # Will trigger retry + +# Exponential backoff +@app.function( + retries=modal.Retries( + max_retries=5, + backoff_coefficient=2.0, # Double delay each retry + initial_delay=1.0, # Start with 1 second + max_delay=30.0 # Cap at 30 seconds + ) +) +def exponential_backoff_task(): + """Task with exponential backoff: 1s, 2s, 4s, 8s, 16s delays""" + return call_rate_limited_service() + +# Custom backoff configuration +@app.function( + retries=modal.Retries( + max_retries=6, + backoff_coefficient=1.5, # 50% increase each retry + initial_delay=2.0, # Start with 2 seconds + max_delay=60.0 # Cap at 1 minute + ) +) +def custom_backoff_task(): + """Custom retry pattern: 2s, 3s, 4.5s, 6.75s, 10.125s, 15.1875s""" + return perform_database_operation() + +# Retry with error handling +@app.function( + retries=modal.Retries( + max_retries=3, + initial_delay=1.0, + backoff_coefficient=2.0 + ) +) +def resilient_task(): + """Task with retry and comprehensive error handling""" + attempt = 0 + try: + # Simulate getting current attempt (in real usage, this would be tracked differently) + result = unreliable_external_service() + print(f"Success on attempt {attempt + 1}") + return result + except TemporaryError as e: + print(f"Temporary error on attempt {attempt + 1}: {e}") + raise # Retry + except PermanentError as e: + print(f"Permanent error, not retrying: {e}") + return {"error": "permanent_failure", "message": str(e)} +``` + +## Advanced Scheduling Patterns + +### Multi-Timezone Scheduling + +```python +import modal + +app = modal.App("global-scheduling") + +# Different tasks in different timezones +@app.function(schedule=modal.Cron("0 9 * * *", timezone="America/New_York")) +def us_business_hours(): + """Run at 9 AM Eastern Time""" + process_us_customer_data() + +@app.function(schedule=modal.Cron("0 9 * * *", timezone="Europe/London")) +def uk_business_hours(): + """Run at 9 AM London Time""" + process_uk_customer_data() + +@app.function(schedule=modal.Cron("0 9 * * *", timezone="Asia/Tokyo")) +def japan_business_hours(): + """Run at 9 AM Japan Time""" + process_japan_customer_data() + +@app.function(schedule=modal.Cron("0 0 * * *", timezone="UTC")) +def global_midnight_task(): + """Run at midnight UTC (global coordination)""" + consolidate_global_reports() +``` + +### Cascading Scheduled Tasks + +```python +import modal + +app = modal.App("cascading-tasks") + +# Shared queue for task coordination +task_queue = modal.Queue.persist("cascade-queue") + +@app.function(schedule=modal.Cron("0 2 * * *")) # 2 AM daily +def stage_1_data_extraction(): + """First stage: Extract data""" + print("Stage 1: Starting data extraction...") + raw_data = extract_raw_data() + + # Queue next stage + task_queue.put({ + "stage": "transform", + "data_id": raw_data["id"], + "timestamp": time.time() + }) + + print("Stage 1: Data extraction completed") + +@app.function(schedule=modal.Period(minutes=5)) # Check every 5 minutes +def stage_2_data_transformation(): + """Second stage: Transform data when available""" + try: + task = task_queue.get(timeout=1) # Non-blocking check + if task and task["stage"] == "transform": + print(f"Stage 2: Transforming data {task['data_id']}") + transformed_data = transform_data(task["data_id"]) + + # Queue final stage + task_queue.put({ + "stage": "load", + "data_id": transformed_data["id"], + "timestamp": time.time() + }) + + print("Stage 2: Data transformation completed") + except: + # No tasks available, continue + pass + +@app.function(schedule=modal.Period(minutes=10)) # Check every 10 minutes +def stage_3_data_loading(): + """Final stage: Load data when ready""" + try: + task = task_queue.get(timeout=1) + if task and task["stage"] == "load": + print(f"Stage 3: Loading data {task['data_id']}") + load_data_to_warehouse(task["data_id"]) + send_completion_notification(task["data_id"]) + print("Stage 3: Data loading completed") + except: + pass +``` + +### Conditional Scheduling with Circuit Breaker + +```python +import modal + +app = modal.App("reliable-scheduling") + +# Shared state for circuit breaker +circuit_state = modal.Dict.persist("circuit-breaker") + +@app.function( + schedule=modal.Period(minutes=10), + retries=modal.Retries(max_retries=2, initial_delay=30.0) +) +def monitored_task(): + """Task with circuit breaker pattern""" + # Check circuit breaker state + is_open = circuit_state.get("circuit_open", False) + failure_count = circuit_state.get("failure_count", 0) + last_failure = circuit_state.get("last_failure_time", 0) + + # Circuit breaker logic + if is_open: + # Check if enough time has passed to try again + if time.time() - last_failure > 300: # 5 minutes + print("Circuit breaker: Attempting to close circuit") + circuit_state["circuit_open"] = False + else: + print("Circuit breaker: Circuit is open, skipping execution") + return {"status": "circuit_open"} + + try: + # Attempt the operation + result = potentially_failing_operation() + + # Success - reset failure count + circuit_state["failure_count"] = 0 + circuit_state["circuit_open"] = False + + print("Task completed successfully") + return {"status": "success", "result": result} + + except Exception as e: + # Handle failure + new_failure_count = failure_count + 1 + circuit_state["failure_count"] = new_failure_count + circuit_state["last_failure_time"] = time.time() + + # Open circuit if too many failures + if new_failure_count >= 3: + circuit_state["circuit_open"] = True + print("Circuit breaker: Opening circuit due to repeated failures") + + print(f"Task failed (attempt {new_failure_count}): {e}") + raise # Will trigger retry policy +``` + +### Dynamic Scheduling Based on Load + +```python +import modal + +app = modal.App("adaptive-scheduling") + +# Shared metrics storage +metrics = modal.Dict.persist("system-metrics") + +@app.function(schedule=modal.Period(seconds=30)) +def collect_system_metrics(): + """Collect system metrics every 30 seconds""" + current_load = get_system_load() + queue_depth = get_queue_depth() + error_rate = get_error_rate() + + metrics.update({ + "load": current_load, + "queue_depth": queue_depth, + "error_rate": error_rate, + "timestamp": time.time() + }) + +@app.function(schedule=modal.Period(minutes=1)) +def adaptive_processor(): + """Process tasks with adaptive frequency based on load""" + current_metrics = metrics.get("load", 0) + queue_depth = metrics.get("queue_depth", 0) + + # Adapt processing based on system state + if current_metrics < 0.5 and queue_depth > 100: + # Low load, high queue - process more aggressively + batch_size = 50 + process_batch_tasks(batch_size) + print(f"High throughput mode: processed {batch_size} tasks") + + elif current_metrics > 0.8: + # High load - reduce processing + batch_size = 10 + process_batch_tasks(batch_size) + print(f"Conservative mode: processed {batch_size} tasks") + + else: + # Normal processing + batch_size = 25 + process_batch_tasks(batch_size) + print(f"Normal mode: processed {batch_size} tasks") + +@app.function( + schedule=modal.Period(minutes=5), + retries=modal.Retries(max_retries=3, initial_delay=60.0) +) +def health_based_processing(): + """Processing task that adapts based on system health""" + error_rate = metrics.get("error_rate", 0) + + if error_rate > 0.1: # More than 10% error rate + print("High error rate detected, running diagnostic tasks") + run_system_diagnostics() + return {"status": "diagnostics"} + else: + print("System healthy, running normal processing") + return process_normal_workload() +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/storage-data.md b/.tessl/tiles/tessl/pypi-modal/docs/storage-data.md new file mode 100644 index 000000000..2d147b6c6 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/storage-data.md @@ -0,0 +1,528 @@ +# Storage & Data + +Modal provides comprehensive storage solutions for persisting data across function calls, including volumes, network file systems, key-value stores, queues, and cloud bucket mounts. These storage primitives enable building stateful applications while maintaining the benefits of serverless architecture. + +## Capabilities + +### Volume - Persistent Networked File System + +Persistent networked file system storage that provides POSIX-like file operations and can be mounted to multiple functions simultaneously. + +```python { .api } +class Volume: + @classmethod + def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Volume": + """Load a Volume by its unique name""" + + @classmethod + def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "Volume": + """Create a persistent Volume with given name""" + + @classmethod + def ephemeral(cls, **kwargs) -> "Volume": + """Create an ephemeral Volume that's deleted when not in use""" + + def listdir(self, path: str) -> list[FileEntry]: + """List files and directories at path""" + + def iterdir(self, path: str) -> AsyncIterator[FileEntry]: + """Async iterator over files and directories at path""" + + def put_file( + self, + local_file: Union[str, Path, BinaryIO], + remote_path: str, + *, + progress: Optional[bool] = None + ) -> None: + """Upload a local file to the volume""" + + def put_directory( + self, + local_path: Union[str, Path], + remote_path: str, + *, + pattern: Optional[str] = None, + progress: Optional[bool] = None + ) -> None: + """Upload a local directory to the volume""" + + def get_file(self, remote_path: str, local_file: Union[str, Path, BinaryIO]) -> None: + """Download a file from the volume to local storage""" + + def remove_file(self, path: str, *, recursive: bool = False) -> None: + """Remove a file or directory from the volume""" + + def exists(self, path: str) -> bool: + """Check if a path exists in the volume""" + + def reload(self) -> None: + """Reload the volume to get latest state""" + +class FileEntry: + """Represents a file or directory entry in a volume""" + path: str + type: FileEntryType # FILE, DIRECTORY, SYMLINK, etc. + mtime: int # Modified time as Unix timestamp + size: int # Size in bytes + +class FileEntryType: + """Type of file entry""" + FILE: int + DIRECTORY: int + SYMLINK: int + FIFO: int + SOCKET: int +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +# Create a persistent volume +volume = modal.Volume.persist("my-data-volume") + +@app.function(volumes={"/data": volume}) +def process_data(): + # Files are accessible at /data mount point + with open("/data/input.txt", "r") as f: + content = f.read() + + # Process data and save results + with open("/data/output.txt", "w") as f: + f.write(f"Processed: {content}") + +# Upload files to volume from local machine +@app.local_entrypoint() +def upload_data(): + volume.put_file("local_data.txt", "/input.txt") + volume.put_directory("./datasets", "/datasets") + + # List volume contents + for entry in volume.listdir("/"): + print(f"{entry.path}: {entry.type.name}, {entry.size} bytes") +``` + +### NetworkFileSystem - Shared Networked Storage + +Shared networked file system that allows multiple functions to read and write files concurrently with better performance for frequent access patterns. + +```python { .api } +class NetworkFileSystem: + @classmethod + def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "NetworkFileSystem": + """Load a NetworkFileSystem by its unique name""" + + @classmethod + def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "NetworkFileSystem": + """Create a persistent NetworkFileSystem with given name""" + + @classmethod + def ephemeral(cls, **kwargs) -> "NetworkFileSystem": + """Create an ephemeral NetworkFileSystem""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() +nfs = modal.NetworkFileSystem.persist("shared-cache") + +@app.function(network_file_systems={"/cache": nfs}) +def worker_function(task_id: str): + cache_file = f"/cache/task_{task_id}.json" + + # Check if cached result exists + if os.path.exists(cache_file): + with open(cache_file, "r") as f: + return json.load(f) + + # Process and cache result + result = expensive_computation(task_id) + with open(cache_file, "w") as f: + json.dump(result, f) + + return result +``` + +### Dict - Persistent Key-Value Store + +Distributed key-value store for sharing data between functions with automatic serialization and deserialization. + +```python { .api } +class Dict: + @classmethod + def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Dict": + """Load a Dict by its unique name""" + + @classmethod + def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "Dict": + """Create a persistent Dict with given name""" + + @classmethod + def ephemeral(cls, **kwargs) -> "Dict": + """Create an ephemeral Dict""" + + def get(self, key: str, default: Any = None) -> Any: + """Get value by key, returns default if key doesn't exist""" + + def __getitem__(self, key: str) -> Any: + """Get value by key using dict[key] syntax""" + + def put(self, key: str, value: Any) -> None: + """Put key-value pair""" + + def __setitem__(self, key: str, value: Any) -> None: + """Set value using dict[key] = value syntax""" + + def pop(self, key: str, default: Any = None) -> Any: + """Remove and return value for key""" + + def __delitem__(self, key: str) -> None: + """Delete key using del dict[key] syntax""" + + def update(self, mapping: Mapping[str, Any]) -> None: + """Update multiple key-value pairs""" + + def clear(self) -> None: + """Remove all items from the dict""" + + def len(self) -> int: + """Get number of items in the dict""" + + def __len__(self) -> int: + """Get number of items using len(dict) syntax""" + + def contains(self, key: str) -> bool: + """Check if key exists in dict""" + + def __contains__(self, key: str) -> bool: + """Check if key exists using 'key in dict' syntax""" + + def keys(self) -> list[str]: + """Get all keys as a list""" + + def values(self) -> list[Any]: + """Get all values as a list""" + + def items(self) -> list[tuple[str, Any]]: + """Get all key-value pairs as list of tuples""" + + def iterate_keys(self) -> AsyncIterator[str]: + """Async iterator over all keys""" + + def iterate_values(self) -> AsyncIterator[Any]: + """Async iterator over all values""" + + def iterate_items(self) -> AsyncIterator[tuple[str, Any]]: + """Async iterator over all key-value pairs""" + +class DictInfo: + """Information about a Dict object""" + name: Optional[str] + created_at: datetime + created_by: Optional[str] +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() +shared_dict = modal.Dict.persist("config-store") + +@app.function() +def setup_config(): + # Store configuration data + shared_dict["database_url"] = "postgresql://..." + shared_dict["api_keys"] = {"service_a": "key1", "service_b": "key2"} + shared_dict["feature_flags"] = {"new_feature": True, "beta_mode": False} + +@app.function() +def worker_task(): + # Access shared configuration + db_url = shared_dict["database_url"] + api_keys = shared_dict.get("api_keys", {}) + + # Check feature flag + if shared_dict.get("feature_flags", {}).get("new_feature", False): + return use_new_algorithm() + else: + return use_legacy_algorithm() + +@app.function() +def analytics_function(): + # Update metrics + current_count = shared_dict.get("request_count", 0) + shared_dict["request_count"] = current_count + 1 + + # Store processing results + results = shared_dict.get("daily_results", []) + results.append({"timestamp": time.time(), "processed": 100}) + shared_dict["daily_results"] = results +``` + +### Queue - Distributed Task Queue + +Distributed queue for asynchronous task processing with automatic serialization and FIFO ordering. + +```python { .api } +class Queue: + @classmethod + def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Queue": + """Load a Queue by its unique name""" + + @classmethod + def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "Queue": + """Create a persistent Queue with given name""" + + @classmethod + def ephemeral(cls, **kwargs) -> "Queue": + """Create an ephemeral Queue""" + + def put(self, item: Any, *, block: bool = True) -> None: + """Put an item into the queue""" + + def put_many(self, items: list[Any], *, block: bool = True) -> None: + """Put multiple items into the queue""" + + def get(self, *, block: bool = True, timeout: Optional[float] = None) -> Any: + """Get an item from the queue""" + + def get_many(self, n: int, *, block: bool = True, timeout: Optional[float] = None) -> list[Any]: + """Get multiple items from the queue""" + + def iterate(self, *, timeout: Optional[float] = None) -> AsyncIterator[Any]: + """Async iterator over queue items""" + + def len(self) -> int: + """Get approximate number of items in queue""" + + def __len__(self) -> int: + """Get approximate number of items using len(queue) syntax""" + +class QueueInfo: + """Information about a Queue object""" + name: Optional[str] + created_at: datetime + created_by: Optional[str] +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() +task_queue = modal.Queue.persist("work-queue") + +# Producer function +@app.function() +def generate_tasks(): + tasks = [{"id": i, "data": f"task_{i}"} for i in range(100)] + task_queue.put_many(tasks) + print(f"Added {len(tasks)} tasks to queue") + +# Consumer function +@app.function() +def process_tasks(): + while True: + try: + # Get task with timeout + task = task_queue.get(timeout=30) + + # Process the task + result = expensive_operation(task["data"]) + print(f"Processed task {task['id']}: {result}") + + except queue.Empty: + print("No more tasks, worker stopping") + break + +# Batch consumer +@app.function() +def batch_processor(): + # Process tasks in batches for efficiency + for batch in task_queue.iterate(): + tasks = task_queue.get_many(10) # Get up to 10 tasks + results = [process_single_task(task) for task in tasks] + print(f"Processed batch of {len(results)} tasks") +``` + +### CloudBucketMount - Cloud Storage Integration + +Mount cloud storage buckets (S3, GCS, Azure) as file systems within Modal functions. + +```python { .api } +class CloudBucketMount: + @classmethod + def from_s3_bucket( + cls, + bucket_name: str, + *, + key_prefix: str = "", + secret: Optional["Secret"] = None, + read_only: bool = True + ) -> "CloudBucketMount": + """Mount an S3 bucket""" + + @classmethod + def from_gcs_bucket( + cls, + bucket_name: str, + *, + key_prefix: str = "", + secret: Optional["Secret"] = None, + read_only: bool = True + ) -> "CloudBucketMount": + """Mount a Google Cloud Storage bucket""" + + @classmethod + def from_azure_blob_storage( + cls, + account_name: str, + container_name: str, + *, + key_prefix: str = "", + secret: Optional["Secret"] = None, + read_only: bool = True + ) -> "CloudBucketMount": + """Mount an Azure Blob Storage container""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App() + +# Mount S3 bucket +s3_mount = modal.CloudBucketMount.from_s3_bucket( + "my-data-bucket", + secret=modal.Secret.from_name("aws-credentials"), + read_only=False +) + +@app.function(cloud_bucket_mounts={"/s3-data": s3_mount}) +def process_s3_data(): + # Access S3 files as local files + with open("/s3-data/input/data.csv", "r") as f: + data = f.read() + + # Process data + result = analyze_data(data) + + # Write results back to S3 + with open("/s3-data/output/results.json", "w") as f: + json.dump(result, f) + +# Mount GCS bucket +gcs_mount = modal.CloudBucketMount.from_gcs_bucket( + "my-gcs-bucket", + secret=modal.Secret.from_name("gcp-credentials") +) + +@app.function(cloud_bucket_mounts={"/gcs-data": gcs_mount}) +def backup_to_gcs(): + # Read local data + local_files = os.listdir("/tmp/data") + + # Copy to GCS through mount + for filename in local_files: + shutil.copy(f"/tmp/data/{filename}", f"/gcs-data/backup/{filename}") +``` + +## Storage Patterns + +### Data Pipeline with Multiple Storage Types + +```python +import modal + +app = modal.App() + +# Different storage for different use cases +raw_data_volume = modal.Volume.persist("raw-data") # Large file storage +processed_cache = modal.NetworkFileSystem.persist("cache") # Fast shared access +config_dict = modal.Dict.persist("pipeline-config") # Configuration +task_queue = modal.Queue.persist("processing-queue") # Task coordination + +@app.function( + volumes={"/raw": raw_data_volume}, + network_file_systems={"/cache": processed_cache} +) +def data_pipeline(): + # Get configuration + batch_size = config_dict.get("batch_size", 100) + + # Process raw data files + for filename in os.listdir("/raw/input"): + # Check cache first + cache_key = f"/cache/processed_{filename}" + if os.path.exists(cache_key): + continue # Already processed + + # Process file + with open(f"/raw/input/{filename}", "r") as f: + data = f.read() + + result = process_data_file(data) + + # Cache result + with open(cache_key, "w") as f: + json.dump(result, f) + + # Queue downstream tasks + task_queue.put({"type": "notify", "file": filename, "result": result}) +``` + +### Shared State Between Functions + +```python +import modal + +app = modal.App() +shared_state = modal.Dict.persist("worker-state") + +@app.function() +def coordinator(): + # Initialize shared state + shared_state["active_workers"] = 0 + shared_state["total_processed"] = 0 + shared_state["status"] = "starting" + + # Start workers + for i in range(5): + worker.spawn(f"worker-{i}") + +@app.function() +def worker(worker_id: str): + # Register worker + current_workers = shared_state.get("active_workers", 0) + shared_state["active_workers"] = current_workers + 1 + + try: + # Do work + for task in get_tasks(): + result = process_task(task) + + # Update shared counters + total = shared_state.get("total_processed", 0) + shared_state["total_processed"] = total + 1 + + finally: + # Unregister worker + current_workers = shared_state.get("active_workers", 0) + shared_state["active_workers"] = max(0, current_workers - 1) + + # Check if all workers done + if shared_state["active_workers"] == 0: + shared_state["status"] = "completed" +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/utility-classes.md b/.tessl/tiles/tessl/pypi-modal/docs/utility-classes.md new file mode 100644 index 000000000..221119b66 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/utility-classes.md @@ -0,0 +1,791 @@ +# Utility Classes + +Modal provides essential utility classes for error handling and file pattern matching. These utilities support robust error management and flexible file filtering capabilities across Modal applications. + +## Capabilities + +### Package Version + +Package version string for the Modal client library. + +```python { .api } +__version__: str # Current package version (e.g., "1.1.4") +``` + +### Error - Base Exception Class + +Base exception class for all Modal-specific errors, providing a hierarchy of specialized error types for different failure scenarios. + +```python { .api } +class Error(Exception): + """Base class for all Modal errors""" + +# Specialized error types +class AlreadyExistsError(Error): + """Raised when a resource creation conflicts with an existing resource""" + +class RemoteError(Error): + """Raised when an error occurs on the Modal server""" + +class TimeoutError(Error): + """Base class for Modal timeouts""" + +class FunctionTimeoutError(TimeoutError): + """Raised when a Function exceeds its execution duration limit""" + +class SandboxTimeoutError(TimeoutError): + """Raised when a Sandbox exceeds its execution duration limit""" + +class VolumeUploadTimeoutError(TimeoutError): + """Raised when a Volume upload times out""" + +class MountUploadTimeoutError(TimeoutError): + """Raised when a Mount upload times out""" + +class InteractiveTimeoutError(TimeoutError): + """Raised when interactive frontends time out while connecting""" + +class OutputExpiredError(TimeoutError): + """Raised when the Output exceeds expiration""" + +class AuthError(Error): + """Raised when a client has missing or invalid authentication""" + +class ConnectionError(Error): + """Raised when an issue occurs while connecting to Modal servers""" + +class InvalidError(Error): + """Raised when user does something invalid""" + +class VersionError(Error): + """Raised when the current client version is unsupported""" + +class NotFoundError(Error): + """Raised when a requested resource was not found""" + +class ExecutionError(Error): + """Raised when something unexpected happened during runtime""" + +class DeserializationError(Error): + """Raised when an error is encountered during deserialization""" + +class SerializationError(Error): + """Raised when an error is encountered during serialization""" +``` + +#### Usage Examples + +```python +import modal + +app = modal.App("error-handling") + +@app.function() +def error_prone_function(data: dict): + """Function demonstrating Modal error handling""" + + try: + # Operation that might fail with different error types + result = risky_operation(data) + return {"success": True, "result": result} + + except modal.FunctionTimeoutError as e: + print(f"Function timed out: {e}") + return {"error": "timeout", "message": str(e)} + + except modal.DeserializationError as e: + print(f"Data deserialization failed: {e}") + return {"error": "invalid_data", "message": str(e)} + + except modal.RemoteError as e: + print(f"Server error occurred: {e}") + return {"error": "server_error", "message": str(e)} + + except modal.Error as e: + # Catch any Modal-specific error + print(f"Modal error: {e}") + return {"error": "modal_error", "type": type(e).__name__, "message": str(e)} + + except Exception as e: + # Non-Modal errors + print(f"Unexpected error: {e}") + return {"error": "unexpected", "message": str(e)} + +@app.function() +def resource_creation_function(resource_name: str): + """Function that handles resource creation errors""" + + try: + # Attempt to create a new resource + volume = modal.Volume.persist(resource_name) + return {"success": True, "resource": resource_name} + + except modal.AlreadyExistsError: + # Resource already exists - use existing one + print(f"Volume {resource_name} already exists, using existing volume") + volume = modal.Volume.from_name(resource_name) + return {"success": True, "resource": resource_name, "status": "existing"} + + except modal.AuthError as e: + print(f"Authentication failed: {e}") + return {"error": "auth_failed", "message": str(e)} + + except modal.InvalidError as e: + print(f"Invalid resource name: {e}") + return {"error": "invalid_name", "message": str(e)} + +@app.function() +def upload_with_timeout_handling(local_path: str, remote_path: str): + """Function that handles upload timeout errors""" + + volume = modal.Volume.from_name("upload-volume") + + try: + volume.put_file(local_path, remote_path) + return {"success": True, "uploaded": remote_path} + + except modal.VolumeUploadTimeoutError as e: + print(f"Volume upload timed out: {e}") + # Implement retry logic or alternative approach + return {"error": "upload_timeout", "retry_recommended": True} + + except modal.NotFoundError as e: + print(f"File not found: {e}") + return {"error": "file_not_found", "path": local_path} + +# Custom error handling with context +def handle_modal_errors(func): + """Decorator for comprehensive Modal error handling""" + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except modal.TimeoutError as e: + return {"error": "timeout", "type": type(e).__name__, "message": str(e)} + except modal.AuthError as e: + return {"error": "authentication", "message": str(e)} + except modal.ConnectionError as e: + return {"error": "connection", "message": str(e)} + except modal.Error as e: + return {"error": "modal", "type": type(e).__name__, "message": str(e)} + except Exception as e: + return {"error": "unknown", "message": str(e)} + return wrapper + +@app.function() +@handle_modal_errors +def decorated_function(data: str): + """Function with decorator-based error handling""" + return process_data_safely(data) + +@app.local_entrypoint() +def main(): + # Test error handling + result1 = error_prone_function.remote({"test": "data"}) + print("Error handling result:", result1) + + # Test resource creation + result2 = resource_creation_function.remote("test-volume") + print("Resource creation result:", result2) + + # Test upload timeout handling + result3 = upload_with_timeout_handling.remote("local_file.txt", "/remote/path.txt") + print("Upload result:", result3) +``` + +### FilePatternMatcher - File Pattern Matching + +Pattern matching utility for filtering files based on glob patterns, similar to Docker's pattern matching with support for exclusion and complex patterns. + +```python { .api } +class FilePatternMatcher: + def __init__(self, *patterns: str) -> None: + """Create a file pattern matcher from glob patterns""" + + def __call__(self, path: Path) -> bool: + """Check if a path matches any of the patterns""" + + def __invert__(self) -> "FilePatternMatcher": + """Invert the matcher to exclude matching patterns""" + + def can_prune_directories(self) -> bool: + """Check if directory pruning is safe for optimization""" +``` + +#### Usage Examples + +```python +import modal +from pathlib import Path + +app = modal.App("pattern-matching") + +# Basic pattern matching +python_matcher = modal.FilePatternMatcher("*.py") +assert python_matcher(Path("script.py")) +assert not python_matcher(Path("README.md")) + +# Multiple patterns +code_matcher = modal.FilePatternMatcher("*.py", "*.js", "*.ts") +assert code_matcher(Path("app.js")) +assert code_matcher(Path("component.ts")) +assert not code_matcher(Path("config.json")) + +# Complex patterns with wildcards +deep_matcher = modal.FilePatternMatcher("**/*.py", "src/**/*.js") +assert deep_matcher(Path("deep/nested/script.py")) +assert deep_matcher(Path("src/components/app.js")) + +# Inverted patterns (exclusion) +non_python_matcher = ~modal.FilePatternMatcher("*.py") +assert not non_python_matcher(Path("script.py")) +assert non_python_matcher(Path("README.md")) + +@app.function() +def filter_files_function(file_paths: list[str]) -> dict: + """Function that filters files using pattern matching""" + + # Create different matchers for different file types + source_files = modal.FilePatternMatcher("*.py", "*.js", "*.ts", "*.java") + config_files = modal.FilePatternMatcher("*.json", "*.yaml", "*.yml", "*.toml") + doc_files = modal.FilePatternMatcher("*.md", "*.txt", "*.rst") + + # Exclude certain patterns + exclude_matcher = modal.FilePatternMatcher( + "**/__pycache__/**", + "**/node_modules/**", + "**/.git/**", + "*.pyc", + "*.tmp" + ) + + results = { + "source_files": [], + "config_files": [], + "doc_files": [], + "other_files": [], + "excluded_files": [] + } + + for file_path_str in file_paths: + file_path = Path(file_path_str) + + # Check if file should be excluded + if exclude_matcher(file_path): + results["excluded_files"].append(file_path_str) + continue + + # Categorize files by type + if source_files(file_path): + results["source_files"].append(file_path_str) + elif config_files(file_path): + results["config_files"].append(file_path_str) + elif doc_files(file_path): + results["doc_files"].append(file_path_str) + else: + results["other_files"].append(file_path_str) + + return results + +@app.function() +def advanced_pattern_matching(directory_paths: list[str]) -> dict: + """Advanced pattern matching with multiple criteria""" + + # Different matchers for different purposes + test_files = modal.FilePatternMatcher("**/test_*.py", "**/*_test.py", "**/tests/**/*.py") + build_artifacts = modal.FilePatternMatcher( + "**/build/**", + "**/dist/**", + "**/*.egg-info/**" + ) + hidden_files = modal.FilePatternMatcher(".*", "**/.*/**") + + # Inverted matchers for inclusion patterns + include_source = ~modal.FilePatternMatcher( + "**/__pycache__/**", + "**/.*/**", + "**/node_modules/**" + ) + + analysis = { + "test_files": [], + "build_artifacts": [], + "hidden_files": [], + "source_files": [], + "total_processed": 0 + } + + for dir_path_str in directory_paths: + dir_path = Path(dir_path_str) + + # Walk through directory structure + if dir_path.exists() and dir_path.is_dir(): + for file_path in dir_path.rglob("*"): + if file_path.is_file(): + analysis["total_processed"] += 1 + relative_path = file_path.relative_to(dir_path) + + # Categorize files + if test_files(relative_path): + analysis["test_files"].append(str(relative_path)) + elif build_artifacts(relative_path): + analysis["build_artifacts"].append(str(relative_path)) + elif hidden_files(relative_path): + analysis["hidden_files"].append(str(relative_path)) + elif include_source(relative_path): + analysis["source_files"].append(str(relative_path)) + + return analysis + +@app.function() +def custom_file_processing(): + """Demonstrate custom pattern matching logic""" + + # Create matcher for Python files, excluding tests + python_no_tests = modal.FilePatternMatcher("*.py") & ~modal.FilePatternMatcher("test_*.py", "*_test.py") + + # Process files with custom logic + files_to_process = [ + "app.py", + "test_app.py", + "utils.py", + "config_test.py", + "main.py" + ] + + results = [] + for filename in files_to_process: + file_path = Path(filename) + + if python_no_tests(file_path): + # Custom processing for source files + result = { + "file": filename, + "type": "source", + "processed": True, + "action": "analyze_code" + } + else: + result = { + "file": filename, + "type": "test" if "test" in filename else "other", + "processed": False, + "action": "skip" + } + + results.append(result) + + return results + +# Utility function for complex pattern combinations +def create_project_filter(): + """Create a comprehensive project file filter""" + + # Include source files + include_patterns = modal.FilePatternMatcher( + "**/*.py", + "**/*.js", + "**/*.ts", + "**/*.json", + "**/*.yaml", + "**/*.yml", + "**/*.md" + ) + + # Exclude build artifacts and temporary files + exclude_patterns = modal.FilePatternMatcher( + "**/__pycache__/**", + "**/node_modules/**", + "**/.git/**", + "**/build/**", + "**/dist/**", + "**/*.pyc", + "**/*.pyo", + "**/*.egg-info/**", + "**/coverage/**", + "**/.pytest_cache/**" + ) + + # Combine patterns: include source files but exclude artifacts + return lambda path: include_patterns(path) and not exclude_patterns(path) + +@app.local_entrypoint() +def main(): + # Test file filtering + sample_files = [ + "src/app.py", + "src/utils.py", + "tests/test_app.py", + "config.json", + "README.md", + "build/output.js", + "__pycache__/app.cpython-39.pyc", + "node_modules/package/index.js" + ] + + filter_result = filter_files_function.remote(sample_files) + print("File filtering result:", filter_result) + + # Test advanced pattern matching + directories = ["./src", "./tests", "./config"] + advanced_result = advanced_pattern_matching.remote(directories) + print("Advanced pattern matching:", advanced_result) + + # Test custom processing + custom_result = custom_file_processing.remote() + print("Custom processing result:", custom_result) +``` + +## Advanced Utility Patterns + +### Comprehensive Error Recovery System + +```python +import modal +import time +import random + +app = modal.App("error-recovery") + +class RetryableError(modal.Error): + """Custom error type for retryable operations""" + pass + +def with_error_recovery(max_retries: int = 3, base_delay: float = 1.0): + """Decorator for automatic error recovery""" + def decorator(func): + def wrapper(*args, **kwargs): + last_error = None + + for attempt in range(max_retries + 1): + try: + return func(*args, **kwargs) + + except modal.TimeoutError as e: + last_error = e + if attempt < max_retries: + delay = base_delay * (2 ** attempt) # Exponential backoff + print(f"Timeout on attempt {attempt + 1}, retrying in {delay}s") + time.sleep(delay) + else: + print(f"All {max_retries + 1} attempts failed due to timeout") + + except modal.ConnectionError as e: + last_error = e + if attempt < max_retries: + delay = base_delay * (1.5 ** attempt) + print(f"Connection error on attempt {attempt + 1}, retrying in {delay}s") + time.sleep(delay) + else: + print(f"All {max_retries + 1} attempts failed due to connection issues") + + except RetryableError as e: + last_error = e + if attempt < max_retries: + delay = base_delay + random.uniform(0, 1) # Jittered delay + print(f"Retryable error on attempt {attempt + 1}, retrying in {delay:.2f}s") + time.sleep(delay) + else: + print(f"All {max_retries + 1} attempts failed due to retryable errors") + + except modal.Error as e: + # Non-retryable Modal errors + print(f"Non-retryable Modal error: {type(e).__name__}: {e}") + raise + + except Exception as e: + # Unknown errors + print(f"Unknown error: {type(e).__name__}: {e}") + raise + + # All retries exhausted + raise last_error + + return wrapper + return decorator + +@app.function() +@with_error_recovery(max_retries=5, base_delay=2.0) +def resilient_operation(data: dict): + """Operation with comprehensive error recovery""" + + # Simulate various failure modes + failure_mode = data.get("failure_mode", "none") + + if failure_mode == "timeout": + raise modal.FunctionTimeoutError("Simulated timeout") + elif failure_mode == "connection": + raise modal.ConnectionError("Simulated connection error") + elif failure_mode == "retryable": + raise RetryableError("Simulated retryable error") + elif failure_mode == "auth": + raise modal.AuthError("Simulated auth error") # Non-retryable + + # Success case + return {"status": "success", "data": data} + +@app.local_entrypoint() +def main(): + # Test different error scenarios + test_cases = [ + {"data": {"value": 1}, "expected": "success"}, + {"data": {"value": 2, "failure_mode": "timeout"}, "expected": "retry_success"}, + {"data": {"value": 3, "failure_mode": "connection"}, "expected": "retry_success"}, + {"data": {"value": 4, "failure_mode": "retryable"}, "expected": "retry_success"}, + {"data": {"value": 5, "failure_mode": "auth"}, "expected": "immediate_failure"}, + ] + + for i, test_case in enumerate(test_cases): + print(f"\nTest case {i + 1}: {test_case['expected']}") + try: + result = resilient_operation.remote(test_case["data"]) + print(f"Result: {result}") + except Exception as e: + print(f"Final error: {type(e).__name__}: {e}") +``` + +### Intelligent File Processing Pipeline + +```python +import modal +from pathlib import Path +import json + +app = modal.App("file-processing-pipeline") + +class FileProcessor: + """Advanced file processing with pattern matching""" + + def __init__(self): + # Define processing rules based on file patterns + self.processors = { + "python": { + "matcher": modal.FilePatternMatcher("*.py"), + "action": self.process_python_file, + "priority": 1 + }, + "javascript": { + "matcher": modal.FilePatternMatcher("*.js", "*.ts"), + "action": self.process_js_file, + "priority": 1 + }, + "config": { + "matcher": modal.FilePatternMatcher("*.json", "*.yaml", "*.yml"), + "action": self.process_config_file, + "priority": 2 + }, + "documentation": { + "matcher": modal.FilePatternMatcher("*.md", "*.rst", "*.txt"), + "action": self.process_doc_file, + "priority": 3 + }, + "tests": { + "matcher": modal.FilePatternMatcher("**/test_*.py", "**/*_test.py", "**/tests/**/*.py"), + "action": self.process_test_file, + "priority": 0 # Highest priority + } + } + + # Files to exclude from processing + self.exclude_matcher = modal.FilePatternMatcher( + "**/__pycache__/**", + "**/node_modules/**", + "**/.git/**", + "**/venv/**", + "**/*.pyc" + ) + + def process_python_file(self, file_path: Path) -> dict: + """Process Python source files""" + return { + "type": "python", + "analysis": "syntax_check_passed", + "metrics": {"lines": 100, "functions": 5}, + "issues": [] + } + + def process_js_file(self, file_path: Path) -> dict: + """Process JavaScript/TypeScript files""" + return { + "type": "javascript", + "analysis": "linting_passed", + "metrics": {"lines": 75, "functions": 3}, + "issues": [] + } + + def process_config_file(self, file_path: Path) -> dict: + """Process configuration files""" + return { + "type": "config", + "analysis": "valid_format", + "validation": "schema_compliant", + "issues": [] + } + + def process_doc_file(self, file_path: Path) -> dict: + """Process documentation files""" + return { + "type": "documentation", + "analysis": "spell_check_passed", + "metrics": {"word_count": 500}, + "issues": [] + } + + def process_test_file(self, file_path: Path) -> dict: + """Process test files""" + return { + "type": "test", + "analysis": "test_discovery_complete", + "metrics": {"test_count": 10, "coverage": 85.5}, + "issues": [] + } + + def classify_file(self, file_path: Path) -> tuple[str, dict]: + """Classify file and return appropriate processor""" + # Check exclusions first + if self.exclude_matcher(file_path): + return "excluded", {} + + # Find matching processor with highest priority + matches = [] + for name, processor in self.processors.items(): + if processor["matcher"](file_path): + matches.append((processor["priority"], name, processor)) + + if matches: + # Sort by priority (lower number = higher priority) + matches.sort(key=lambda x: x[0]) + _, name, processor = matches[0] + return name, processor + + return "unknown", {} + +@app.function() +def intelligent_file_processing(file_paths: list[str]) -> dict: + """Process files using intelligent pattern matching""" + + processor = FileProcessor() + results = { + "processed": {}, + "excluded": [], + "unknown": [], + "errors": [], + "statistics": { + "total_files": len(file_paths), + "processed_count": 0, + "excluded_count": 0, + "unknown_count": 0, + "error_count": 0 + } + } + + for file_path_str in file_paths: + try: + file_path = Path(file_path_str) + file_type, processor_info = processor.classify_file(file_path) + + if file_type == "excluded": + results["excluded"].append(file_path_str) + results["statistics"]["excluded_count"] += 1 + + elif file_type == "unknown": + results["unknown"].append(file_path_str) + results["statistics"]["unknown_count"] += 1 + + else: + # Process the file + process_result = processor_info["action"](file_path) + results["processed"][file_path_str] = { + "file_type": file_type, + "priority": processor_info["priority"], + "result": process_result + } + results["statistics"]["processed_count"] += 1 + + except Exception as e: + error_info = { + "file": file_path_str, + "error": str(e), + "error_type": type(e).__name__ + } + results["errors"].append(error_info) + results["statistics"]["error_count"] += 1 + + return results + +@app.function() +def batch_file_analysis(directory_paths: list[str]) -> dict: + """Analyze entire directories with pattern-based filtering""" + + processor = FileProcessor() + analysis = { + "directories": {}, + "summary": { + "total_directories": len(directory_paths), + "total_files": 0, + "file_type_counts": {}, + "processing_time": 0 + } + } + + start_time = time.time() + + for dir_path_str in directory_paths: + dir_path = Path(dir_path_str) + dir_analysis = { + "files": [], + "file_counts": {}, + "excluded_files": [] + } + + if dir_path.exists() and dir_path.is_dir(): + for file_path in dir_path.rglob("*"): + if file_path.is_file(): + analysis["summary"]["total_files"] += 1 + relative_path = file_path.relative_to(dir_path) + + file_type, processor_info = processor.classify_file(relative_path) + + if file_type == "excluded": + dir_analysis["excluded_files"].append(str(relative_path)) + else: + dir_analysis["files"].append({ + "path": str(relative_path), + "type": file_type, + "priority": processor_info.get("priority", 999) + }) + + # Update counts + dir_analysis["file_counts"][file_type] = dir_analysis["file_counts"].get(file_type, 0) + 1 + analysis["summary"]["file_type_counts"][file_type] = analysis["summary"]["file_type_counts"].get(file_type, 0) + 1 + + analysis["directories"][dir_path_str] = dir_analysis + + analysis["summary"]["processing_time"] = time.time() - start_time + return analysis + +@app.local_entrypoint() +def main(): + # Test intelligent file processing + sample_files = [ + "src/main.py", + "src/utils.py", + "tests/test_main.py", + "config/settings.json", + "docs/README.md", + "package.json", + "node_modules/lib/index.js", # Should be excluded + "__pycache__/main.cpython-39.pyc", # Should be excluded + "data/sample.csv", # Unknown type + "Dockerfile" # Unknown type + ] + + processing_result = intelligent_file_processing.remote(sample_files) + print("Intelligent processing result:") + print(json.dumps(processing_result, indent=2)) + + # Test batch directory analysis + directories = ["./src", "./tests", "./config"] + analysis_result = batch_file_analysis.remote(directories) + print("\nBatch analysis result:") + print(json.dumps(analysis_result, indent=2)) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md b/.tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md new file mode 100644 index 000000000..bee992136 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md @@ -0,0 +1,567 @@ +# Web & API Integration + +Modal provides comprehensive web application serving capabilities, enabling deployment of web servers, REST APIs, and integration with popular web frameworks. These decorators transform Modal functions into web endpoints that can handle HTTP requests and serve web applications. + +## Capabilities + +### ASGI Application Support + +Decorator for serving ASGI-compatible applications like FastAPI, Starlette, and modern async web frameworks. + +```python { .api } +def asgi_app(func: Callable) -> Callable: + """Decorator to serve ASGI applications (FastAPI, Starlette, etc.)""" +``` + +#### Usage Examples + +```python +import modal +from fastapi import FastAPI + +app = modal.App("fastapi-server") +web_app = FastAPI() + +@web_app.get("/") +def read_root(): + return {"message": "Hello from Modal FastAPI!"} + +@web_app.get("/items/{item_id}") +def read_item(item_id: int, q: str = None): + return {"item_id": item_id, "q": q} + +@web_app.post("/process") +def process_data(data: dict): + # Expensive processing that benefits from Modal's scaling + result = expensive_computation(data) + return {"result": result} + +@app.function() +@modal.asgi_app() +def fastapi_app(): + return web_app + +# The FastAPI app is now served on Modal's infrastructure +``` + +### WSGI Application Support + +Decorator for serving WSGI-compatible applications like Flask, Django, and traditional web frameworks. + +```python { .api } +def wsgi_app(func: Callable) -> Callable: + """Decorator to serve WSGI applications (Flask, Django, etc.)""" +``` + +#### Usage Examples + +```python +import modal +from flask import Flask, request, jsonify + +app = modal.App("flask-server") +web_app = Flask(__name__) + +@web_app.route("/") +def hello(): + return jsonify({"message": "Hello from Modal Flask!"}) + +@web_app.route("/upload", methods=["POST"]) +def upload_file(): + if 'file' not in request.files: + return jsonify({"error": "No file uploaded"}), 400 + + file = request.files['file'] + # Process file with Modal's compute resources + result = process_uploaded_file(file) + return jsonify({"processed": result}) + +@web_app.route("/predict", methods=["POST"]) +def make_prediction(): + data = request.get_json() + # Use Modal for ML inference + prediction = run_ml_model(data) + return jsonify({"prediction": prediction}) + +@app.function() +@modal.wsgi_app() +def flask_app(): + return web_app + +# The Flask app is now served on Modal's infrastructure +``` + +### Web Endpoint + +Decorator for creating simple HTTP endpoints without requiring a full web framework. + +```python { .api } +def web_endpoint(func: Callable) -> Callable: + """Decorator to create HTTP web endpoints""" +``` + +#### Usage Examples + +```python +import modal +import json + +app = modal.App("simple-api") + +@app.function() +@modal.web_endpoint(method="GET") +def get_status(): + """Simple GET endpoint""" + return { + "status": "healthy", + "timestamp": time.time(), + "version": "1.0.0" + } + +@app.function() +@modal.web_endpoint(method="POST") +def process_webhook(data=None): + """Handle webhook POST requests""" + if data: + # Process webhook data + result = handle_webhook_data(data) + return {"success": True, "processed": result} + else: + return {"error": "No data provided"}, 400 + +@app.function() +@modal.web_endpoint(method="GET", path="/metrics") +def get_metrics(): + """Custom metrics endpoint""" + metrics = collect_application_metrics() + return {"metrics": metrics} + +# Endpoints are available at: +# GET https://your-app.modal.run/get_status +# POST https://your-app.modal.run/process_webhook +# GET https://your-app.modal.run/metrics +``` + +### FastAPI Endpoint + +Specialized decorator optimized for FastAPI applications with enhanced integration features. + +```python { .api } +def fastapi_endpoint(func: Callable) -> Callable: + """Decorator specifically for FastAPI endpoints with enhanced features""" +``` + +#### Usage Examples + +```python +import modal +from fastapi import FastAPI, HTTPException, Depends +from pydantic import BaseModel + +app = modal.App("advanced-fastapi") + +class PredictionRequest(BaseModel): + text: str + model: str = "default" + +class PredictionResponse(BaseModel): + prediction: str + confidence: float + model_used: str + +@app.function() +@modal.fastapi_endpoint() +def ml_prediction_api(): + """Advanced FastAPI endpoint with automatic docs and validation""" + fastapi_app = FastAPI( + title="ML Prediction API", + description="Machine learning predictions powered by Modal", + version="1.0.0" + ) + + @fastapi_app.post("/predict", response_model=PredictionResponse) + async def predict(request: PredictionRequest): + try: + # Load model and make prediction + model = load_ml_model(request.model) + prediction, confidence = model.predict(request.text) + + return PredictionResponse( + prediction=prediction, + confidence=confidence, + model_used=request.model + ) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + + @fastapi_app.get("/models") + async def list_models(): + """List available models""" + models = get_available_models() + return {"models": models} + + return fastapi_app + +# Automatic OpenAPI docs available at: +# https://your-app.modal.run/docs +# https://your-app.modal.run/redoc +``` + +### Web Server + +Decorator for creating custom web servers with full control over the HTTP handling. + +```python { .api } +def web_server(func: Callable) -> Callable: + """Decorator to create custom web servers""" +``` + +#### Usage Examples + +```python +import modal +from http.server import HTTPServer, BaseHTTPRequestHandler +import json + +app = modal.App("custom-server") + +class CustomHandler(BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/health": + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + response = {"status": "healthy", "server": "custom"} + self.wfile.write(json.dumps(response).encode()) + else: + self.send_response(404) + self.end_headers() + + def do_POST(self): + if self.path == "/process": + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + + try: + data = json.loads(post_data.decode('utf-8')) + result = custom_processing(data) + + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps({"result": result}).encode()) + except Exception as e: + self.send_response(500) + self.end_headers() + +@app.function() +@modal.web_server() +def custom_web_server(): + """Custom HTTP server with full control""" + server = HTTPServer(("0.0.0.0", 8000), CustomHandler) + server.serve_forever() +``` + +## Advanced Web Patterns + +### Multi-Framework API Gateway + +```python +import modal +from fastapi import FastAPI +from flask import Flask +import asyncio + +app = modal.App("api-gateway") + +# FastAPI for async endpoints +fastapi_app = FastAPI() + +@fastapi_app.get("/async-data") +async def get_async_data(): + # Async data processing + data = await fetch_external_data() + return {"data": data, "type": "async"} + +# Flask for sync endpoints +flask_app = Flask(__name__) + +@flask_app.route("/sync-data") +def get_sync_data(): + # Synchronous data processing + data = fetch_local_data() + return {"data": data, "type": "sync"} + +@app.function() +@modal.asgi_app() +def async_api(): + return fastapi_app + +@app.function() +@modal.wsgi_app() +def sync_api(): + return flask_app + +# Different apps serve different endpoints +# https://your-app-async.modal.run/async-data +# https://your-app-sync.modal.run/sync-data +``` + +### Microservices Architecture + +```python +import modal +from fastapi import FastAPI + +app = modal.App("microservices") + +# User service +user_app = FastAPI(title="User Service") + +@user_app.get("/users/{user_id}") +def get_user(user_id: int): + return get_user_from_database(user_id) + +@user_app.post("/users") +def create_user(user_data: dict): + return create_user_in_database(user_data) + +# Order service +order_app = FastAPI(title="Order Service") + +@order_app.get("/orders/{order_id}") +def get_order(order_id: int): + return get_order_from_database(order_id) + +@order_app.post("/orders") +def create_order(order_data: dict): + # Validate user exists + user_id = order_data.get("user_id") + user = get_user_from_database(user_id) + if not user: + raise HTTPException(status_code=404, detail="User not found") + + return create_order_in_database(order_data) + +# Deploy as separate services +@app.function() +@modal.asgi_app() +def user_service(): + return user_app + +@app.function() +@modal.asgi_app() +def order_service(): + return order_app +``` + +### WebSocket and Real-time Features + +```python +import modal +from fastapi import FastAPI, WebSocket +from typing import List + +app = modal.App("realtime-app") +web_app = FastAPI() + +class ConnectionManager: + def __init__(self): + self.active_connections: List[WebSocket] = [] + + async def connect(self, websocket: WebSocket): + await websocket.accept() + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def broadcast(self, message: str): + for connection in self.active_connections: + await connection.send_text(message) + +manager = ConnectionManager() + +@web_app.websocket("/ws") +async def websocket_endpoint(websocket: WebSocket): + await manager.connect(websocket) + try: + while True: + data = await websocket.receive_text() + # Process message and broadcast to all connections + processed = process_realtime_data(data) + await manager.broadcast(f"Processed: {processed}") + except Exception: + manager.disconnect(websocket) + +@web_app.post("/broadcast") +def broadcast_message(message: dict): + """HTTP endpoint to broadcast messages""" + asyncio.create_task(manager.broadcast(json.dumps(message))) + return {"status": "broadcasted"} + +@app.function() +@modal.asgi_app() +def realtime_app(): + return web_app +``` + +### File Upload and Processing API + +```python +import modal +from fastapi import FastAPI, File, UploadFile, HTTPException +from typing import List +import tempfile +import os + +app = modal.App("file-processor") +web_app = FastAPI(title="File Processing API") + +@web_app.post("/upload/single") +async def upload_single_file(file: UploadFile = File(...)): + """Upload and process a single file""" + if not file.filename.endswith(('.jpg', '.png', '.pdf', '.txt')): + raise HTTPException(status_code=400, detail="Unsupported file type") + + # Save file temporarily + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + content = await file.read() + tmp_file.write(content) + tmp_path = tmp_file.name + + try: + # Process file with Modal compute + if file.filename.endswith(('.jpg', '.png')): + result = process_image(tmp_path) + elif file.filename.endswith('.pdf'): + result = extract_pdf_text(tmp_path) + else: + result = process_text_file(tmp_path) + + return { + "filename": file.filename, + "size": len(content), + "processed": result + } + finally: + os.unlink(tmp_path) + +@web_app.post("/upload/batch") +async def upload_multiple_files(files: List[UploadFile] = File(...)): + """Upload and process multiple files""" + results = [] + + for file in files: + try: + # Process each file + content = await file.read() + result = await process_file_async(file.filename, content) + results.append({ + "filename": file.filename, + "status": "success", + "result": result + }) + except Exception as e: + results.append({ + "filename": file.filename, + "status": "error", + "error": str(e) + }) + + return {"processed_files": results} + +@web_app.get("/download/{file_id}") +async def download_processed_file(file_id: str): + """Download processed file results""" + file_path = get_processed_file_path(file_id) + if not os.path.exists(file_path): + raise HTTPException(status_code=404, detail="File not found") + + return FileResponse(file_path) + +@app.function() +@modal.asgi_app() +def file_processing_api(): + return web_app +``` + +### Authentication and Middleware Integration + +```python +import modal +from fastapi import FastAPI, Depends, HTTPException, status +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from fastapi.middleware.cors import CORSMiddleware +import jwt + +app = modal.App("secure-api") +web_app = FastAPI(title="Secure API with Authentication") + +# Add CORS middleware +web_app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Configure appropriately for production + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +security = HTTPBearer() + +def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)): + """Verify JWT token""" + try: + payload = jwt.decode( + credentials.credentials, + os.environ["JWT_SECRET"], + algorithms=["HS256"] + ) + return payload + except jwt.ExpiredSignatureError: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Token expired" + ) + except jwt.InvalidTokenError: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid token" + ) + +@web_app.post("/login") +def login(credentials: dict): + """Authenticate user and return JWT token""" + if authenticate_user(credentials["username"], credentials["password"]): + token = create_jwt_token(credentials["username"]) + return {"access_token": token, "token_type": "bearer"} + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid credentials" + ) + +@web_app.get("/protected") +def protected_endpoint(current_user: dict = Depends(verify_token)): + """Protected endpoint requiring authentication""" + return { + "message": f"Hello {current_user['username']}!", + "data": get_user_specific_data(current_user['user_id']) + } + +@web_app.get("/admin") +def admin_endpoint(current_user: dict = Depends(verify_token)): + """Admin-only endpoint""" + if not current_user.get("is_admin"): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Admin access required" + ) + + return {"admin_data": get_admin_data()} + +@app.function(secrets=[modal.Secret.from_name("jwt-secret")]) +@modal.asgi_app() +def secure_api(): + return web_app +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/tile.json b/.tessl/tiles/tessl/pypi-modal/tile.json new file mode 100644 index 000000000..1b7cf22dc --- /dev/null +++ b/.tessl/tiles/tessl/pypi-modal/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-modal", + "version": "1.1.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/modal@1.1.4", + "summary": "Python client library for Modal, a serverless cloud computing platform that enables developers to run Python code in the cloud with on-demand access to compute resources." +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-openai/docs/assistants.md b/.tessl/tiles/tessl/pypi-openai/docs/assistants.md new file mode 100644 index 000000000..c1dc5c0ab --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/assistants.md @@ -0,0 +1,540 @@ +# Assistants API + +Build AI assistants with advanced capabilities including code interpretation, file search, and function calling. Assistants maintain conversation state and can access tools to help users accomplish tasks. + +## Capabilities + +### Create Assistant + +Create an AI assistant with specific instructions and tools. + +```python { .api } +def create( + self, + *, + model: str, + description: str | Omit = omit, + instructions: str | Omit = omit, + metadata: dict[str, str] | Omit = omit, + name: str | Omit = omit, + reasoning_effort: str | Omit = omit, + response_format: dict | Omit = omit, + temperature: float | Omit = omit, + tool_resources: dict | Omit = omit, + tools: list[dict] | Omit = omit, + top_p: float | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Assistant: + """ + Create an AI assistant. + + Args: + model: Model ID to use (e.g., "gpt-4", "gpt-3.5-turbo"). + + description: Description of the assistant (max 512 chars). + + instructions: System instructions that guide assistant behavior (max 256k chars). + + metadata: Key-value pairs for storing additional info (max 16). + Keys max 64 chars, values max 512 chars. + + name: Name of the assistant (max 256 chars). + + reasoning_effort: Constrains effort on reasoning for reasoning models. + Currently supported values are "none", "minimal", "low", "medium", and "high". + Reducing reasoning effort can result in faster responses and fewer tokens used. + - gpt-5.1 defaults to "none" (no reasoning). Supports: none, low, medium, high. + - Models before gpt-5.1 default to "medium" and do not support "none". + - gpt-5-pro defaults to (and only supports) "high" reasoning effort. + + response_format: Output format specification. + - {"type": "text"}: Plain text (default) + - {"type": "json_object"}: Valid JSON + - {"type": "json_schema", "json_schema": {...}}: Structured output + + temperature: Sampling temperature between 0 and 2. Default 1. + + tool_resources: Resources for tools. Options: + - {"code_interpreter": {"file_ids": [...]}} + - {"file_search": {"vector_store_ids": [...]}} + + tools: List of enabled tools (maximum of 128 tools per assistant). Options: + - {"type": "code_interpreter"}: Python code execution + - {"type": "file_search"}: Search uploaded files + - {"type": "function", "function": {...}}: Custom functions + + top_p: Nucleus sampling parameter between 0 and 1. Default 1. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Assistant: Created assistant. + + Raises: + BadRequestError: Invalid parameters + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic assistant +assistant = client.beta.assistants.create( + name="Math Tutor", + instructions="You are a helpful math tutor. Explain concepts clearly.", + model="gpt-4" +) + +print(f"Assistant ID: {assistant.id}") + +# With code interpreter +assistant = client.beta.assistants.create( + name="Data Analyst", + instructions="Analyze data and create visualizations.", + model="gpt-4", + tools=[{"type": "code_interpreter"}] +) + +# With file search +assistant = client.beta.assistants.create( + name="Documentation Helper", + instructions="Help users find information in documentation.", + model="gpt-4", + tools=[{"type": "file_search"}], + tool_resources={ + "file_search": { + "vector_store_ids": ["vs_abc123"] + } + } +) + +# With function calling +tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get current weather", + "parameters": { + "type": "object", + "properties": { + "location": {"type": "string"} + }, + "required": ["location"] + } + } + } +] + +assistant = client.beta.assistants.create( + name="Weather Assistant", + instructions="Help users check weather.", + model="gpt-4", + tools=tools +) + +# With structured output +assistant = client.beta.assistants.create( + name="Structured Assistant", + instructions="Generate structured responses.", + model="gpt-4", + response_format={ + "type": "json_schema", + "json_schema": { + "name": "response", + "strict": True, + "schema": { + "type": "object", + "properties": { + "answer": {"type": "string"} + }, + "required": ["answer"], + "additionalProperties": False + } + } + } +) + +# With metadata +assistant = client.beta.assistants.create( + name="Customer Support", + instructions="Assist customers with inquiries.", + model="gpt-4", + metadata={ + "department": "support", + "version": "1.0" + } +) + +# With reasoning effort (for reasoning models) +assistant = client.beta.assistants.create( + name="Reasoning Assistant", + instructions="Solve complex problems with reasoning.", + model="gpt-5.1", + reasoning_effort="high" +) +``` + +### Retrieve Assistant + +Get assistant details. + +```python { .api } +def retrieve( + self, + assistant_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Assistant: + """ + Retrieve assistant details. + + Args: + assistant_id: The ID of the assistant. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Assistant: Assistant details. + + Raises: + NotFoundError: Assistant not found + """ +``` + +Usage example: + +```python +assistant = client.beta.assistants.retrieve("asst_abc123") + +print(f"Name: {assistant.name}") +print(f"Model: {assistant.model}") +print(f"Tools: {assistant.tools}") +``` + +### Update Assistant + +Modify assistant properties. + +```python { .api } +def update( + self, + assistant_id: str, + *, + description: str | Omit = omit, + instructions: str | Omit = omit, + metadata: dict[str, str] | Omit = omit, + model: str | Omit = omit, + name: str | Omit = omit, + reasoning_effort: str | Omit = omit, + response_format: dict | Omit = omit, + temperature: float | Omit = omit, + tool_resources: dict | Omit = omit, + tools: list[dict] | Omit = omit, + top_p: float | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Assistant: + """ + Update assistant properties. + + Args: Same as create method, but all optional. + + Returns: + Assistant: Updated assistant. + """ +``` + +Usage example: + +```python +# Update instructions +assistant = client.beta.assistants.update( + "asst_abc123", + instructions="Updated instructions." +) + +# Add tools +assistant = client.beta.assistants.update( + "asst_abc123", + tools=[ + {"type": "code_interpreter"}, + {"type": "file_search"} + ] +) +``` + +### List Assistants + +List all assistants with pagination. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Assistant]: + """ + List assistants with pagination. + + Args: + after: Cursor for next page. + before: Cursor for previous page. + limit: Number to retrieve (max 100). Default 20. + order: Sort order. "asc" or "desc". Default "desc". + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[Assistant]: Paginated list of assistants. + """ +``` + +Usage example: + +```python +# List all assistants +assistants = client.beta.assistants.list() + +for assistant in assistants: + print(f"{assistant.name} ({assistant.id})") + +# Pagination +page1 = client.beta.assistants.list(limit=10) +page2 = client.beta.assistants.list(limit=10, after=page1.data[-1].id) +``` + +### Delete Assistant + +Delete an assistant. + +```python { .api } +def delete( + self, + assistant_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> AssistantDeleted: + """ + Delete an assistant. + + Args: + assistant_id: The ID of the assistant to delete. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + AssistantDeleted: Deletion confirmation. + + Raises: + NotFoundError: Assistant not found + """ +``` + +Usage example: + +```python +result = client.beta.assistants.delete("asst_abc123") + +print(f"Deleted: {result.deleted}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Assistant(BaseModel): + """AI Assistant.""" + id: str + created_at: int + description: str | None + instructions: str | None + metadata: dict[str, str] | None + model: str + name: str | None + object: Literal["assistant"] + tools: list[dict] + response_format: dict | None + temperature: float | None + tool_resources: dict | None + top_p: float | None + +class AssistantDeleted(BaseModel): + """Deletion confirmation.""" + id: str + deleted: bool + object: Literal["assistant.deleted"] + +# Tool types +class CodeInterpreterTool(TypedDict): + type: Literal["code_interpreter"] + +class FileSearchTool(TypedDict): + type: Literal["file_search"] + +class FunctionTool(TypedDict): + type: Literal["function"] + function: FunctionDefinition + +class FunctionDefinition(TypedDict): + name: str + description: NotRequired[str] + parameters: dict # JSON Schema + strict: NotRequired[bool] + +# Tool resources +class ToolResources(TypedDict, total=False): + code_interpreter: CodeInterpreterResources + file_search: FileSearchResources + +class CodeInterpreterResources(TypedDict, total=False): + file_ids: list[str] + +class FileSearchResources(TypedDict, total=False): + vector_store_ids: list[str] +``` + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Create specialized assistants +support_assistant = client.beta.assistants.create( + name="Customer Support", + instructions="Friendly and helpful support agent.", + model="gpt-4", + tools=[{"type": "file_search"}] +) + +code_assistant = client.beta.assistants.create( + name="Code Helper", + instructions="Help with coding questions.", + model="gpt-4", + tools=[{"type": "code_interpreter"}] +) + +# 2. Organize with metadata +assistant = client.beta.assistants.create( + name="Sales Bot", + model="gpt-4", + metadata={ + "team": "sales", + "region": "us-west", + "version": "2.0" + } +) + +# 3. Update as needed +assistant = client.beta.assistants.update( + assistant.id, + instructions="Updated instructions based on feedback." +) + +# 4. Clean up unused assistants +assistants = client.beta.assistants.list() +for assistant in assistants: + if should_delete(assistant): + client.beta.assistants.delete(assistant.id) +``` + +## Complete Example + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Create assistant +assistant = client.beta.assistants.create( + name="Math Tutor", + instructions="You are a helpful math tutor.", + model="gpt-4", + tools=[{"type": "code_interpreter"}] +) + +# 2. Create thread +thread = client.beta.threads.create() + +# 3. Add message +message = client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="Can you help me solve x^2 + 5x + 6 = 0?" +) + +# 4. Run assistant +run = client.beta.threads.runs.create( + thread_id=thread.id, + assistant_id=assistant.id +) + +# 5. Wait for completion +import time + +while run.status not in ["completed", "failed"]: + time.sleep(1) + run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) + +# 6. Get response +messages = client.beta.threads.messages.list(thread_id=thread.id) + +for message in messages.data: + if message.role == "assistant": + print(message.content[0].text.value) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_assistant(): + client = AsyncOpenAI() + + assistant = await client.beta.assistants.create( + name="Async Assistant", + model="gpt-4", + instructions="I help async." + ) + + return assistant.id + +assistant_id = asyncio.run(create_assistant()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/audio.md b/.tessl/tiles/tessl/pypi-openai/docs/audio.md new file mode 100644 index 000000000..863376385 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/audio.md @@ -0,0 +1,579 @@ +# Audio + +Convert audio to text (transcription and translation) and text to speech using Whisper and TTS models. Supports multiple audio formats and languages. + +## Capabilities + +### Transcription + +Convert audio to text in the original language using the Whisper model. + +```python { .api } +def create( + self, + *, + file: FileTypes, + model: str | AudioModel, + chunking_strategy: dict | str | Omit = omit, + include: list[str] | Omit = omit, + known_speaker_names: list[str] | Omit = omit, + known_speaker_references: list[str] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: Literal["json", "text", "srt", "verbose_json", "vtt", "diarized_json"] | Omit = omit, + stream: bool | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: list[Literal["word", "segment"]] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Transcription | TranscriptionVerbose: + """ + Transcribe audio to text in the original language. + + Args: + file: Audio file to transcribe. Supported formats: flac, mp3, mp4, mpeg, + mpga, m4a, ogg, wav, webm. Max file size: 25 MB. + Can be file path string, file object, or tuple. + + model: Model ID. Options: + - "gpt-4o-transcribe": Advanced transcription with streaming support + - "gpt-4o-mini-transcribe": Faster, cost-effective transcription + - "gpt-4o-transcribe-diarize": Speaker diarization model + - "whisper-1": Powered by open source Whisper V2 model + + chunking_strategy: Controls how audio is cut into chunks. Options: + - "auto": Server normalizes loudness and uses voice activity detection (VAD) + - {"type": "server_vad", ...}: Manually configure VAD parameters + - If unset: Audio transcribed as a single block + - Required for gpt-4o-transcribe-diarize with inputs >30 seconds + + include: Additional information to include. Options: + - "logprobs": Returns log probabilities for confidence analysis + - Only works with response_format="json" + - Only supported for gpt-4o-transcribe and gpt-4o-mini-transcribe + - Not supported with gpt-4o-transcribe-diarize + + known_speaker_names: List of speaker names for diarization (e.g., ["customer", "agent"]). + Corresponds to audio samples in known_speaker_references. Up to 4 speakers. + Used with gpt-4o-transcribe-diarize model. + + known_speaker_references: List of audio samples (as data URLs) containing known speaker + references. Each sample must be 2-10 seconds. Matches known_speaker_names. + Used with gpt-4o-transcribe-diarize model. + + language: Language of the audio in ISO-639-1 format (e.g., "en", "fr", "de"). + Providing language improves accuracy and latency. + + prompt: Optional text to guide the model's style or continue previous segment. + Should match audio language. + + response_format: Output format. Options: + - "json": JSON with text (default) + - "text": Plain text only + - "srt": SubRip subtitle format + - "verbose_json": JSON with segments, timestamps, confidence + - "vtt": WebVTT subtitle format + - "diarized_json": JSON with speaker annotations (for gpt-4o-transcribe-diarize) + Note: gpt-4o-transcribe/mini only support "json". gpt-4o-transcribe-diarize + supports "json", "text", and "diarized_json" (required for speaker annotations). + + stream: If true, model response will be streamed using server-sent events. + Returns Stream[TranscriptionStreamEvent]. Not supported for whisper-1. + + temperature: Sampling temperature between 0 and 1. Higher values increase + randomness. Default is 0. + + timestamp_granularities: Timestamp precision options. + - ["segment"]: Segment-level timestamps (default) + - ["word"]: Word-level timestamps + - ["segment", "word"]: Both levels + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Transcription: Basic response with text + TranscriptionVerbose: Detailed response with segments and timestamps + + Raises: + BadRequestError: Invalid file format or size + AuthenticationError: Invalid API key + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic transcription +with open("audio.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file + ) +print(transcript.text) + +# With language hint for better accuracy +with open("french_audio.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + language="fr" + ) + +# Verbose JSON with detailed information +with open("audio.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + response_format="verbose_json", + timestamp_granularities=["word", "segment"] + ) + +print(f"Duration: {transcript.duration}") +print(f"Language: {transcript.language}") + +for segment in transcript.segments: + print(f"[{segment.start:.2f}s - {segment.end:.2f}s]: {segment.text}") + +for word in transcript.words: + print(f"{word.word} ({word.start:.2f}s)") + +# SRT subtitle format +with open("video_audio.mp3", "rb") as audio_file: + srt = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + response_format="srt" + ) +# Save to file +with open("subtitles.srt", "w") as f: + f.write(srt) + +# With prompt for context/style +with open("continuation.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="whisper-1", + file=audio_file, + prompt="Previous text for context..." + ) + +# Using file_from_path helper +from openai import OpenAI +from openai import file_from_path + +client = OpenAI() + +transcript = client.audio.transcriptions.create( + model="whisper-1", + file=file_from_path("audio.mp3") +) + +# Advanced: Using gpt-4o-transcribe with streaming +with open("audio.mp3", "rb") as audio_file: + stream = client.audio.transcriptions.create( + model="gpt-4o-transcribe", + file=audio_file, + stream=True + ) + for event in stream: + print(event.text, end="", flush=True) + +# Advanced: Speaker diarization with gpt-4o-transcribe-diarize +with open("meeting.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="gpt-4o-transcribe-diarize", + file=audio_file, + response_format="diarized_json", + chunking_strategy="auto" + ) + for segment in transcript.segments: + print(f"[{segment.speaker}]: {segment.text}") + +# Advanced: With known speaker references +with open("call.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="gpt-4o-transcribe-diarize", + file=audio_file, + response_format="diarized_json", + known_speaker_names=["customer", "agent"], + known_speaker_references=[ + "data:audio/mp3;base64,...", # Customer voice sample + "data:audio/mp3;base64,..." # Agent voice sample + ] + ) + +# Advanced: Using include for confidence scores +with open("audio.mp3", "rb") as audio_file: + transcript = client.audio.transcriptions.create( + model="gpt-4o-transcribe", + file=audio_file, + response_format="json", + include=["logprobs"] + ) + # Access logprobs for confidence analysis +``` + +### Translation + +Translate audio to English text using the Whisper model. + +```python { .api } +def create( + self, + *, + file: FileTypes, + model: str | AudioModel, + prompt: str | Omit = omit, + response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | Omit = omit, + temperature: float | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Translation | TranslationVerbose | str: + """ + Translate audio to English text. + + Args: + file: Audio file to translate. Supported formats: flac, mp3, mp4, mpeg, + mpga, m4a, ogg, wav, webm. Max file size: 25 MB. + + model: Model ID. Currently only "whisper-1" is available. + + prompt: Optional text to guide the model's style. + + response_format: Output format. Options: + - "json": JSON with text (default) + - "text": Plain text only + - "srt": SubRip subtitle format + - "verbose_json": JSON with segments and details + - "vtt": WebVTT subtitle format + + temperature: Sampling temperature between 0 and 1. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Translation: Basic response with English text (for json format) + TranslationVerbose: Detailed response with segments (for verbose_json format) + str: Plain text string (for text, srt, vtt formats) + + Raises: + BadRequestError: Invalid file format or size + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Translate French audio to English +with open("french_audio.mp3", "rb") as audio_file: + translation = client.audio.translations.create( + model="whisper-1", + file=audio_file + ) +print(translation.text) + +# Verbose format with segments +with open("spanish_audio.mp3", "rb") as audio_file: + translation = client.audio.translations.create( + model="whisper-1", + file=audio_file, + response_format="verbose_json" + ) + +for segment in translation.segments: + print(f"[{segment.start:.2f}s]: {segment.text}") +``` + +### Text-to-Speech + +Convert text to spoken audio using TTS models. + +```python { .api } +def create( + self, + *, + input: str, + model: str | SpeechModel, + voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], + instructions: str | Omit = omit, + response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, + speed: float | Omit = omit, + stream_format: Literal["sse", "audio"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> HttpxBinaryResponseContent: + """ + Convert text to spoken audio. + + Args: + input: Text to convert to audio. Max length: 4096 characters. + + model: TTS model to use. Options: + - "tts-1": Standard quality, faster, lower cost + - "tts-1-hd": High definition quality, slower, higher cost + - "gpt-4o-mini-tts": Advanced model with instruction support + + voice: Voice to use for generation. Options: + - "alloy": Neutral, balanced + - "ash": Clear and articulate + - "ballad": Warm and expressive + - "coral": Bright and engaging + - "echo": Calm and measured + - "sage": Wise and authoritative + - "shimmer": Soft and gentle + - "verse": Dynamic and versatile + - "marin": Smooth and professional + - "cedar": Rich and grounded + + instructions: Control the voice with additional instructions. + Does not work with tts-1 or tts-1-hd. Only supported by gpt-4o-mini-tts. + + response_format: Audio format. Options: + - "mp3": Default, good compression + - "opus": Best for streaming, lower latency + - "aac": Good compression, widely supported + - "flac": Lossless compression + - "wav": Uncompressed + - "pcm": Raw 16-bit PCM audio + + speed: Playback speed between 0.25 and 4.0. Default 1.0. + + stream_format: Format to stream the audio in. Options: + - "sse": Server-sent events streaming + - "audio": Raw audio streaming + Note: "sse" not supported for tts-1 or tts-1-hd. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + HttpxBinaryResponseContent: Audio file content. Use .content for bytes, + .read() for streaming, .stream_to_file(path) for direct save. + + Raises: + BadRequestError: Invalid parameters or text too long + """ +``` + +Usage examples: + +```python +from openai import OpenAI +from pathlib import Path + +client = OpenAI() + +# Basic TTS +response = client.audio.speech.create( + model="tts-1", + voice="alloy", + input="Hello! This is a test of text to speech." +) + +# Save to file +speech_file = Path("output.mp3") +response.stream_to_file(speech_file) + +# Different voices +voices = ["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] +for voice in voices: + response = client.audio.speech.create( + model="tts-1", + voice=voice, + input="Testing different voices." + ) + response.stream_to_file(f"voice_{voice}.mp3") + +# High quality audio (using marin or cedar recommended for best quality) +response = client.audio.speech.create( + model="tts-1-hd", + voice="marin", + input="High definition audio output." +) +response.stream_to_file("hd_output.mp3") + +# Streaming optimized format (Opus) +response = client.audio.speech.create( + model="tts-1", + voice="shimmer", + input="Optimized for streaming.", + response_format="opus" +) +response.stream_to_file("output.opus") + +# Adjust playback speed +response = client.audio.speech.create( + model="tts-1", + voice="alloy", + input="This will play faster.", + speed=1.5 +) +response.stream_to_file("fast_speech.mp3") + +# Get raw bytes +response = client.audio.speech.create( + model="tts-1", + voice="echo", + input="Getting raw audio bytes." +) +audio_bytes = response.content +# Process bytes as needed + +# Streaming response +response = client.audio.speech.create( + model="tts-1", + voice="ballad", + input="Streaming audio data." +) + +with open("streaming.mp3", "wb") as f: + for chunk in response.iter_bytes(): + f.write(chunk) + +# Advanced: Using gpt-4o-mini-tts with instructions +response = client.audio.speech.create( + model="gpt-4o-mini-tts", + voice="sage", + input="This is a test of voice control.", + instructions="Speak in a warm, friendly tone with slight enthusiasm." +) +response.stream_to_file("instructed_speech.mp3") + +# Advanced: Server-sent events streaming +response = client.audio.speech.create( + model="gpt-4o-mini-tts", + voice="coral", + input="Real-time audio streaming.", + stream_format="sse" +) +response.stream_to_file("sse_output.mp3") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +# Transcription types +class Transcription(BaseModel): + text: str + +class TranscriptionVerbose(BaseModel): + text: str + language: str + duration: float + segments: list[TranscriptionSegment] | None + words: list[TranscriptionWord] | None + +class TranscriptionSegment(BaseModel): + id: int + seek: int + start: float + end: float + text: str + tokens: list[int] + temperature: float + avg_logprob: float + compression_ratio: float + no_speech_prob: float + +class TranscriptionWord(BaseModel): + word: str + start: float + end: float + +class TranscriptionDiarized(BaseModel): + """Transcription with speaker diarization.""" + text: str + language: str + duration: float + segments: list[DiarizedSegment] + +class DiarizedSegment(BaseModel): + """Segment with speaker information.""" + speaker: str # Speaker identifier + start: float + end: float + text: str + +# Translation types +class Translation(BaseModel): + text: str + +class TranslationVerbose(BaseModel): + text: str + language: str + duration: float + segments: list[TranscriptionSegment] | None + +# Model types +AudioModel = Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "gpt-4o-transcribe-diarize", "whisper-1"] +SpeechModel = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts"] + +# File types +FileTypes = Union[ + FileContent, # File-like object + tuple[str | None, FileContent], # (filename, content) + tuple[str | None, FileContent, str | None] # (filename, content, content_type) +] + +# Response type for TTS +class HttpxBinaryResponseContent: + content: bytes + def read(self) -> bytes: ... + def iter_bytes(self, chunk_size: int = None) -> Iterator[bytes]: ... + def stream_to_file(self, file_path: str | Path) -> None: ... +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def transcribe_audio(): + client = AsyncOpenAI() + + with open("audio.mp3", "rb") as audio_file: + transcript = await client.audio.transcriptions.create( + model="whisper-1", + file=audio_file + ) + return transcript.text + +async def generate_speech(): + client = AsyncOpenAI() + + response = await client.audio.speech.create( + model="tts-1", + voice="alloy", + input="Async text to speech" + ) + response.stream_to_file("async_output.mp3") + +# Run async operations +text = asyncio.run(transcribe_audio()) +asyncio.run(generate_speech()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/batches.md b/.tessl/tiles/tessl/pypi-openai/docs/batches.md new file mode 100644 index 000000000..bf7c3c163 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/batches.md @@ -0,0 +1,526 @@ +# Batch Processing + +Submit batch requests for asynchronous processing of multiple API calls. Process large volumes of requests cost-effectively with 50% discount on costs and 24-hour completion window. + +## Capabilities + +### Create Batch + +Create a batch request for async processing. + +```python { .api } +def create( + self, + *, + completion_window: str, + endpoint: str, + input_file_id: str, + metadata: dict[str, str] | Omit = omit, + output_expires_after: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Batch: + """ + Create a batch request for asynchronous processing. + + Args: + completion_window: Time frame for completion. Currently only "24h". + + endpoint: API endpoint for batch requests. Supported: + - "/v1/responses": Response API (structured outputs) + - "/v1/chat/completions": Chat completions + - "/v1/embeddings": Embeddings (max 50,000 inputs per batch) + - "/v1/completions": Text completions + - "/v1/moderations": Moderations + + input_file_id: ID of uploaded JSONL file with requests. + Each line: {"custom_id": "...", "method": "POST", "url": "...", "body": {...}} + + metadata: Optional key-value pairs (max 16). Keys max 64 chars, values max 512 chars. + + output_expires_after: Expiration policy for output and error files. + Dict with keys: + - anchor: "created_at" (file creation time) + - seconds: int (3600-2592000, i.e., 1 hour to 30 days) + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Batch: Created batch with status "validating". + + Raises: + BadRequestError: Invalid file format or endpoint + NotFoundError: Input file not found + """ +``` + +Usage examples: + +```python +from openai import OpenAI +import json + +client = OpenAI() + +# Create batch input file (JSONL format) +batch_requests = [ + { + "custom_id": "request-1", + "method": "POST", + "url": "/v1/chat/completions", + "body": { + "model": "gpt-3.5-turbo", + "messages": [{"role": "user", "content": "What is 2+2?"}] + } + }, + { + "custom_id": "request-2", + "method": "POST", + "url": "/v1/chat/completions", + "body": { + "model": "gpt-3.5-turbo", + "messages": [{"role": "user", "content": "What is the capital of France?"}] + } + } +] + +# Write to JSONL file +with open("batch_requests.jsonl", "w") as f: + for request in batch_requests: + f.write(json.dumps(request) + "\n") + +# Upload file +with open("batch_requests.jsonl", "rb") as f: + batch_file = client.files.create(file=f, purpose="batch") + +# Create batch +batch = client.batches.create( + input_file_id=batch_file.id, + endpoint="/v1/chat/completions", + completion_window="24h" +) + +print(f"Batch ID: {batch.id}") +print(f"Status: {batch.status}") + +# With metadata +batch = client.batches.create( + input_file_id=batch_file.id, + endpoint="/v1/chat/completions", + completion_window="24h", + metadata={ + "experiment": "test-run-1", + "dataset": "evaluation-set" + } +) + +# With output file expiration (e.g., 7 days) +batch = client.batches.create( + input_file_id=batch_file.id, + endpoint="/v1/chat/completions", + completion_window="24h", + output_expires_after={ + "anchor": "created_at", + "seconds": 604800 # 7 days + } +) + +# Embeddings batch +embeddings_requests = [ + { + "custom_id": "embed-1", + "method": "POST", + "url": "/v1/embeddings", + "body": { + "model": "text-embedding-3-small", + "input": "Sample text 1" + } + }, + { + "custom_id": "embed-2", + "method": "POST", + "url": "/v1/embeddings", + "body": { + "model": "text-embedding-3-small", + "input": "Sample text 2" + } + } +] + +with open("embed_requests.jsonl", "w") as f: + for request in embeddings_requests: + f.write(json.dumps(request) + "\n") + +with open("embed_requests.jsonl", "rb") as f: + embed_file = client.files.create(file=f, purpose="batch") + +batch = client.batches.create( + input_file_id=embed_file.id, + endpoint="/v1/embeddings", + completion_window="24h" +) +``` + +### Retrieve Batch + +Get batch status and results. + +```python { .api } +def retrieve( + self, + batch_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Batch: + """ + Retrieve batch details and status. + + Args: + batch_id: The ID of the batch. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Batch: Batch details with current status. + + Raises: + NotFoundError: Batch not found + """ +``` + +Usage example: + +```python +# Check batch status +batch = client.batches.retrieve("batch_abc123") + +print(f"Status: {batch.status}") +print(f"Total: {batch.request_counts.total}") +print(f"Completed: {batch.request_counts.completed}") +print(f"Failed: {batch.request_counts.failed}") + +if batch.status == "completed": + # Download results + if batch.output_file_id: + result_content = client.files.content(batch.output_file_id) + + # Parse JSONL results + import json + results = [] + for line in result_content.text.split("\n"): + if line.strip(): + results.append(json.loads(line)) + + for result in results: + custom_id = result["custom_id"] + response = result["response"] + print(f"{custom_id}: {response}") + + # Check error file if any failed + if batch.error_file_id: + error_content = client.files.content(batch.error_file_id) + print("Errors:", error_content.text) +``` + +### List Batches + +List batch requests with pagination. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Batch]: + """ + List batches with pagination. + + Args: + after: Cursor for pagination. Return batches after this batch ID. + limit: Number of batches to retrieve (max 100). Default 20. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[Batch]: Paginated list of batches. + """ +``` + +Usage example: + +```python +# List all batches +batches = client.batches.list() + +for batch in batches: + print(f"{batch.id}: {batch.status}") + +# Pagination +page1 = client.batches.list(limit=10) +page2 = client.batches.list(limit=10, after=page1.data[-1].id) + +# Filter by status +completed_batches = [ + b for b in client.batches.list() + if b.status == "completed" +] +``` + +### Cancel Batch + +Cancel an in-progress batch. + +```python { .api } +def cancel( + self, + batch_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Batch: + """ + Cancel a batch that is in progress. + + Args: + batch_id: The ID of the batch to cancel. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Batch: Batch with status "cancelling" or "cancelled". + + Raises: + NotFoundError: Batch not found + BadRequestError: Batch not in cancellable state + """ +``` + +Usage example: + +```python +# Cancel batch +batch = client.batches.cancel("batch_abc123") + +print(f"Status: {batch.status}") # "cancelling" or "cancelled" +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Batch(BaseModel): + """Batch request.""" + id: str + completion_window: str + created_at: int + endpoint: str + input_file_id: str + object: Literal["batch"] + status: Literal[ + "validating", "failed", "in_progress", + "finalizing", "completed", "expired", "cancelling", "cancelled" + ] + cancelled_at: int | None + cancelling_at: int | None + completed_at: int | None + error_file_id: str | None + errors: dict | None + expired_at: int | None + expires_at: int | None + failed_at: int | None + finalizing_at: int | None + in_progress_at: int | None + metadata: dict[str, str] | None + output_file_id: str | None + request_counts: BatchRequestCounts + +class BatchRequestCounts(BaseModel): + """Request count statistics.""" + completed: int + failed: int + total: int + +class OutputExpiresAfter(BaseModel): + """Expiration policy for batch output files.""" + anchor: Literal["created_at"] # File creation time anchor + seconds: int # Expiration time in seconds (3600-2592000) + +# Pagination +class SyncCursorPage[T](BaseModel): + data: list[T] + object: str + first_id: str | None + last_id: str | None + has_more: bool + def __iter__(self) -> Iterator[T]: ... +``` + +## Input File Format + +Batch input JSONL format: + +```jsonl +{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Hello!"}]}} +{"custom_id": "request-2", "method": "POST", "url": "/v1/embeddings", "body": {"model": "text-embedding-3-small", "input": "Hello world"}} +``` + +## Output File Format + +Batch output JSONL format: + +```jsonl +{"id": "batch_req_abc", "custom_id": "request-1", "response": {"status_code": 200, "request_id": "req_xyz", "body": {"id": "chatcmpl-123", "object": "chat.completion", ...}}, "error": null} +{"id": "batch_req_def", "custom_id": "request-2", "response": {"status_code": 200, "request_id": "req_uvw", "body": {"object": "list", "data": [...]}}, "error": null} +``` + +## Best Practices + +```python +from openai import OpenAI +import json +import time + +client = OpenAI() + +# 1. Monitor batch progress +def wait_for_batch(batch_id: str, poll_interval: int = 60): + """Wait for batch to complete.""" + while True: + batch = client.batches.retrieve(batch_id) + + if batch.status in ["completed", "failed", "cancelled", "expired"]: + return batch + + print(f"Status: {batch.status}") + print(f"Progress: {batch.request_counts.completed}/{batch.request_counts.total}") + time.sleep(poll_interval) + +# 2. Process results efficiently +def process_batch_results(batch_id: str): + """Download and process batch results.""" + batch = client.batches.retrieve(batch_id) + + if batch.status != "completed": + raise Exception(f"Batch not completed: {batch.status}") + + # Download results + result_content = client.files.content(batch.output_file_id) + + results = {} + for line in result_content.text.split("\n"): + if line.strip(): + result = json.loads(line) + custom_id = result["custom_id"] + response = result["response"]["body"] + results[custom_id] = response + + return results + +# 3. Handle errors +def get_batch_errors(batch_id: str): + """Get failed requests from batch.""" + batch = client.batches.retrieve(batch_id) + + if not batch.error_file_id: + return [] + + error_content = client.files.content(batch.error_file_id) + + errors = [] + for line in error_content.text.split("\n"): + if line.strip(): + errors.append(json.loads(line)) + + return errors + +# 4. Retry failed requests +def retry_failed_requests(batch_id: str): + """Create new batch with failed requests.""" + errors = get_batch_errors(batch_id) + + if not errors: + return None + + # Create retry input file + with open("retry_requests.jsonl", "w") as f: + for error in errors: + # Reconstruct request from error + request = { + "custom_id": error["custom_id"], + "method": error["method"], + "url": error["url"], + "body": error["body"] + } + f.write(json.dumps(request) + "\n") + + # Upload and create new batch + with open("retry_requests.jsonl", "rb") as f: + retry_file = client.files.create(file=f, purpose="batch") + + return client.batches.create( + input_file_id=retry_file.id, + endpoint=errors[0]["url"], + completion_window="24h", + metadata={"retry_of": batch_id} + ) + +# Complete workflow +# 1. Create batch +batch = create_batch() + +# 2. Wait for completion +completed_batch = wait_for_batch(batch.id) + +# 3. Process results +if completed_batch.status == "completed": + results = process_batch_results(batch.id) + print(f"Processed {len(results)} results") + + # Check for failures + if completed_batch.request_counts.failed > 0: + retry_batch = retry_failed_requests(batch.id) + print(f"Created retry batch: {retry_batch.id}") +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_batch(): + client = AsyncOpenAI() + + batch = await client.batches.create( + input_file_id="file-abc123", + endpoint="/v1/chat/completions", + completion_window="24h" + ) + + return batch.id + +batch_id = asyncio.run(create_batch()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/chat-completions.md b/.tessl/tiles/tessl/pypi-openai/docs/chat-completions.md new file mode 100644 index 000000000..e1b9fad11 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/chat-completions.md @@ -0,0 +1,1018 @@ +# Chat Completions + +Create conversational responses using OpenAI's language models with support for text, vision inputs, audio, function calling, structured outputs, and streaming. Chat completions are the primary interface for interacting with models like GPT-4 and GPT-3.5. + +## Access Patterns + +Chat completions are accessible via: +- `client.chat.completions` - Primary access pattern +- `client.beta.chat.completions` - Beta namespace alias (identical to `client.chat`) + +## Capabilities + +### Create Chat Completion + +Generate a response for a chat conversation with extensive configuration options. + +```python { .api } +def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: str | ChatModel, + audio: ChatCompletionAudioParam | None | Omit = omit, + frequency_penalty: float | None | Omit = omit, + function_call: dict | str | Omit = omit, + functions: Iterable[dict] | Omit = omit, + logit_bias: dict[str, int] | None | Omit = omit, + logprobs: bool | None | Omit = omit, + max_completion_tokens: int | None | Omit = omit, + max_tokens: int | None | Omit = omit, + metadata: dict[str, str] | None | Omit = omit, + modalities: list[Literal["text", "audio"]] | None | Omit = omit, + n: int | None | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: dict | None | Omit = omit, + presence_penalty: float | None | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Literal["in-memory", "24h"] | None | Omit = omit, + reasoning_effort: Literal["none", "minimal", "low", "medium", "high"] | None | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: int | None | Omit = omit, + service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None | Omit = omit, + stop: str | list[str] | None | Omit = omit, + store: bool | None | Omit = omit, + stream: bool | Omit = omit, + stream_options: dict | None | Omit = omit, + temperature: float | None | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: int | None | Omit = omit, + top_p: float | None | Omit = omit, + user: str | Omit = omit, + verbosity: Literal["low", "medium", "high"] | None | Omit = omit, + web_search_options: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatCompletion | Stream[ChatCompletionChunk]: + """ + Create a model response for the given chat conversation. + + Args: + messages: List of messages comprising the conversation. Each message can be: + - System message: {"role": "system", "content": "..."} + - User message: {"role": "user", "content": "..."} + - Assistant message: {"role": "assistant", "content": "..."} + - Tool message: {"role": "tool", "content": "...", "tool_call_id": "..."} + Supports text, images, and audio content. + + model: Model ID like "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo", or "o1". + See https://platform.openai.com/docs/models for available models. + + audio: Parameters for audio output when modalities includes "audio". + {"voice": "alloy|echo|fable|onyx|nova|shimmer", "format": "wav|mp3|flac|opus|pcm16"} + + frequency_penalty: Number between -2.0 and 2.0. Penalizes tokens based on frequency + in the text, reducing repetition. Default 0. + + function_call: (Deprecated, use tool_choice) Controls function calling. + - "none": No function calls + - "auto": Model decides + - {"name": "function_name"}: Force specific function + + functions: (Deprecated, use tools) List of function definitions. + + logit_bias: Modify token probabilities. Maps token IDs to bias values from + -100 to 100. Values near ±1 slightly adjust probability, ±100 ban/force tokens. + + logprobs: If true, returns log probabilities of output tokens. + + max_completion_tokens: Maximum tokens for completion including reasoning tokens. + Preferred over max_tokens for o-series models. + + max_tokens: (Deprecated for o-series) Maximum tokens in completion. + Use max_completion_tokens instead. + + metadata: Up to 16 key-value pairs for storing additional object information. + Keys max 64 chars, values max 512 chars. + + modalities: Output types to generate. Options: ["text"], ["audio"], ["text", "audio"]. + Default ["text"]. Audio requires gpt-4o-audio-preview model. + + n: Number of completion choices to generate. Default 1. + Costs scale with number of choices. + + parallel_tool_calls: Enable parallel function calling during tool use. + Default true when tools are present. + + prediction: Static predicted output for regeneration tasks (e.g., file content). + + presence_penalty: Number between -2.0 and 2.0. Penalizes tokens based on + presence in text, encouraging new topics. Default 0. + + prompt_cache_key: Cache identifier for optimizing similar requests. + Replaces the user field for caching. + + prompt_cache_retention: Cache retention policy. "24h" enables extended caching + up to 24 hours. Default "in-memory". + + reasoning_effort: Effort level for reasoning models (o-series). + Options: "none", "minimal", "low", "medium", "high". + - gpt-5.1: defaults to "none" + - Other models: default "medium" + - gpt-5-pro: only supports "high" + + response_format: Output format specification. + - {"type": "text"}: Plain text (default) + - {"type": "json_object"}: Valid JSON + - {"type": "json_schema", "json_schema": {...}}: Structured Outputs + + safety_identifier: Stable user identifier (hashed) for policy violation detection. + + seed: For deterministic sampling (Beta). Same seed + parameters should return + same result, but not guaranteed. Check system_fingerprint for changes. + + service_tier: Processing type for serving. Options: "auto", "default", "flex", + "scale", "priority". Affects latency and pricing. + + stop: Up to 4 sequences where generation stops. Can be string or list of strings. + + store: If true, stores the completion for model distillation/evals. + + stream: If true, returns SSE stream of ChatCompletionChunk objects. + Returns Stream[ChatCompletionChunk] instead of ChatCompletion. + + stream_options: Streaming configuration. Accepts dict with: + - "include_usage": bool - If true, includes token usage in final chunk + - "include_obfuscation": bool - If true (default), adds random characters + to obfuscation field on streaming delta events to normalize payload sizes + as mitigation for side-channel attacks. Set to false to optimize bandwidth. + + temperature: Sampling temperature between 0 and 2. Higher values (e.g., 0.8) + make output more random, lower values (e.g., 0.2) more deterministic. + Default 1. Alter this or top_p, not both. + + tool_choice: Controls tool/function calling. + - "none": No tools called + - "auto": Model decides (default when tools present) + - "required": Model must call at least one tool + - {"type": "function", "function": {"name": "..."}}: Force specific tool + + tools: List of tools/functions available to the model. Each tool: + { + "type": "function", + "function": { + "name": "function_name", + "description": "What it does", + "parameters": {...} # JSON Schema + } + } + + top_logprobs: Number of most likely tokens to return with logprobs (0-20). + Requires logprobs=true. + + top_p: Nucleus sampling parameter between 0 and 1. Model considers tokens + with top_p probability mass. E.g., 0.1 means only tokens in top 10%. + Default 1. Alter this or temperature, not both. + + user: (Deprecated for caching, use prompt_cache_key) Unique user identifier + for abuse monitoring. + + verbosity: Output detail level for reasoning models. Options: "low", "medium", "high". + + web_search_options: Web search configuration (if available for model). + + extra_headers: Additional HTTP headers for the request. + + extra_query: Additional query parameters for the request. + + extra_body: Additional JSON fields for the request body. + + timeout: Request timeout in seconds. + + Returns: + ChatCompletion: If stream=False (default), returns complete response. + Stream[ChatCompletionChunk]: If stream=True, returns streaming response. + + Raises: + BadRequestError: Invalid parameters + AuthenticationError: Invalid API key + RateLimitError: Rate limit exceeded + APIError: Other API errors + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic chat completion +response = client.chat.completions.create( + model="gpt-4", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the capital of France?"} + ] +) +print(response.choices[0].message.content) + +# With multiple messages and temperature +response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": "You are a creative writer."}, + {"role": "user", "content": "Write a haiku about coding."}, + ], + temperature=0.8, + max_tokens=100 +) + +# With vision (image input) +response = client.chat.completions.create( + model="gpt-4-turbo", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": "What's in this image?"}, + { + "type": "image_url", + "image_url": {"url": "https://example.com/image.jpg"} + } + ] + } + ] +) + +# With function calling / tools +tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get current weather for a location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City name, e.g., San Francisco" + }, + "unit": { + "type": "string", + "enum": ["celsius", "fahrenheit"] + } + }, + "required": ["location"] + } + } + } +] + +response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "What's the weather in Boston?"}], + tools=tools, + tool_choice="auto" +) + +# Check if model wants to call a function +if response.choices[0].message.tool_calls: + for tool_call in response.choices[0].message.tool_calls: + print(f"Function: {tool_call.function.name}") + print(f"Arguments: {tool_call.function.arguments}") + +# Streaming response +stream = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Tell me a story."}], + stream=True +) + +for chunk in stream: + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") + +# With reasoning effort (o-series models) +response = client.chat.completions.create( + model="o1", + messages=[ + {"role": "user", "content": "Solve this complex math problem: ..."} + ], + reasoning_effort="high" +) + +# With structured output (JSON Schema) +response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "List 3 colors"}], + response_format={ + "type": "json_schema", + "json_schema": { + "name": "colors_response", + "strict": True, + "schema": { + "type": "object", + "properties": { + "colors": { + "type": "array", + "items": {"type": "string"} + } + }, + "required": ["colors"], + "additionalProperties": False + } + } + } +) +``` + +### Parse with Structured Output + +Create chat completion with automatic Pydantic model parsing for structured outputs. + +```python { .api } +def parse( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: str | ChatModel, + response_format: type[ResponseFormatT] | Omit = omit, + audio: ChatCompletionAudioParam | None | Omit = omit, + frequency_penalty: float | None | Omit = omit, + function_call: dict | str | Omit = omit, + functions: Iterable[dict] | Omit = omit, + logit_bias: dict[str, int] | None | Omit = omit, + logprobs: bool | None | Omit = omit, + max_completion_tokens: int | None | Omit = omit, + max_tokens: int | None | Omit = omit, + metadata: dict[str, str] | None | Omit = omit, + modalities: list[Literal["text", "audio"]] | None | Omit = omit, + n: int | None | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: dict | None | Omit = omit, + presence_penalty: float | None | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Literal["in-memory", "24h"] | None | Omit = omit, + reasoning_effort: Literal["none", "minimal", "low", "medium", "high"] | None | Omit = omit, + safety_identifier: str | Omit = omit, + seed: int | None | Omit = omit, + service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None | Omit = omit, + stop: str | list[str] | None | Omit = omit, + store: bool | None | Omit = omit, + stream_options: dict | None | Omit = omit, + temperature: float | None | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: int | None | Omit = omit, + top_p: float | None | Omit = omit, + user: str | Omit = omit, + verbosity: Literal["low", "medium", "high"] | None | Omit = omit, + web_search_options: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ParsedChatCompletion[ResponseFormatT]: + """ + Create chat completion with automatic Pydantic model parsing. + + Converts Pydantic model to JSON schema, sends to API, and parses response + back into the model. Also automatically parses function tool calls when + using pydantic_function_tool() or strict mode. + + Args: + messages: List of conversation messages. + model: Model ID to use. + response_format: Pydantic model class for structured output. + (other parameters same as create method) + + Returns: + ParsedChatCompletion[ResponseFormatT]: Completion with parsed content. + Access via completion.choices[0].message.parsed + + Raises: + Same as create method, plus validation errors for malformed responses. + """ +``` + +Usage example: + +```python +from pydantic import BaseModel +from openai import OpenAI + +# Define response structure +class Step(BaseModel): + explanation: str + output: str + +class MathResponse(BaseModel): + steps: list[Step] + final_answer: str + +client = OpenAI() + +# Parse returns strongly-typed response +completion = client.chat.completions.parse( + model="gpt-4", + messages=[ + {"role": "system", "content": "You are a helpful math tutor."}, + {"role": "user", "content": "Solve: 8x + 31 = 2"} + ], + response_format=MathResponse +) + +# Access parsed content with full type safety +message = completion.choices[0].message +if message.parsed: + for step in message.parsed.steps: + print(f"{step.explanation}: {step.output}") + print(f"Answer: {message.parsed.final_answer}") + +# With function tools using pydantic +from openai import pydantic_function_tool + +class WeatherParams(BaseModel): + location: str + unit: Literal["celsius", "fahrenheit"] = "celsius" + +tool = pydantic_function_tool( + WeatherParams, + name="get_weather", + description="Get current weather" +) + +completion = client.chat.completions.parse( + model="gpt-4", + messages=[{"role": "user", "content": "What's the weather in NYC?"}], + tools=[tool], + response_format=MathResponse # For assistant's final response +) + +# Tool calls are automatically parsed +if completion.choices[0].message.tool_calls: + for call in completion.choices[0].message.tool_calls: + if call.type == "function": + # call.function.parsed_arguments is a WeatherParams instance + params = call.function.parsed_arguments + print(f"Location: {params.location}, Unit: {params.unit}") +``` + +### Stored Chat Completions + +Store, retrieve, update, and delete chat completions for persistent conversation management. + +```python { .api } +def retrieve( + self, + completion_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatCompletion: + """ + Retrieve a previously stored chat completion by its ID. + + Args: + completion_id: The ID of the stored chat completion to retrieve. + + Returns: + ChatCompletion: The stored completion object. + """ + +def update( + self, + completion_id: str, + *, + metadata: dict[str, str] | None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatCompletion: + """ + Update metadata for a stored chat completion. + + Args: + completion_id: The ID of the stored completion to update. + metadata: Updated metadata key-value pairs (max 16 pairs). Required parameter. + + Returns: + ChatCompletion: The updated completion object. + """ + +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: dict[str, str] | None | Omit = omit, + model: str | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[ChatCompletion]: + """ + List stored chat completions with cursor-based pagination. + + Args: + after: Cursor for pagination (ID of object to start after). + limit: Maximum number of completions to return (default 20, max 100). + metadata: Filter by metadata key-value pairs. Only returns completions with matching metadata. + model: Filter by model. Only returns completions generated with the specified model. + order: Sort order: "asc" (oldest first) or "desc" (newest first). + + Returns: + SyncCursorPage[ChatCompletion]: Paginated list of completions. + """ + +def delete( + self, + completion_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatCompletionDeleted: + """ + Delete a stored chat completion. + + Args: + completion_id: The ID of the stored completion to delete. + + Returns: + ChatCompletionDeleted: Confirmation of deletion with deleted=True. + """ +``` + +Access stored completion messages: + +```python { .api } +# Via client.chat.completions.messages.list() +def list( + self, + completion_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[ChatCompletionStoreMessage]: + """ + List messages from a stored chat completion. + + Args: + completion_id: The ID of the stored completion. + after: Cursor for pagination. + limit: Maximum number of messages to return. + order: Sort order: "asc" or "desc". + + Returns: + SyncCursorPage[ChatCompletionStoreMessage]: Paginated list of messages. + """ +``` + +#### Usage Example + +```python +from openai import OpenAI + +client = OpenAI() + +# Create a chat completion with store=True +response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Tell me about Python"}], + store=True, + metadata={"user_id": "user123", "session": "abc"} +) + +completion_id = response.id +print(f"Stored completion: {completion_id}") + +# Retrieve the stored completion later +stored = client.chat.completions.retrieve(completion_id) +print(stored.choices[0].message.content) + +# Update metadata +updated = client.chat.completions.update( + completion_id, + metadata={"user_id": "user123", "session": "abc", "reviewed": "true"} +) + +# List all stored completions +page = client.chat.completions.list(limit=10, order="desc") +for completion in page.data: + print(f"{completion.id}: {completion.created}") + +# List messages from a specific completion +messages_page = client.chat.completions.messages.list(completion_id) +for message in messages_page.data: + print(f"{message.role}: {message.content}") + +# Delete when no longer needed +deleted = client.chat.completions.delete(completion_id) +print(f"Deleted: {deleted.deleted}") +``` + +### Stream Chat Completions + +Wrapper over `create(stream=True)` that provides a more granular event API and automatic accumulation of deltas. Requires use within a context manager. + +```python { .api } +def stream( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: str | ChatModel, + audio: ChatCompletionAudioParam | None | Omit = omit, + frequency_penalty: float | None | Omit = omit, + function_call: dict | str | Omit = omit, + functions: Iterable[dict] | Omit = omit, + logit_bias: dict[str, int] | None | Omit = omit, + logprobs: bool | None | Omit = omit, + max_completion_tokens: int | None | Omit = omit, + max_tokens: int | None | Omit = omit, + metadata: dict[str, str] | None | Omit = omit, + modalities: list[Literal["text", "audio"]] | None | Omit = omit, + n: int | None | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: dict | None | Omit = omit, + presence_penalty: float | None | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Literal["in-memory", "24h"] | None | Omit = omit, + reasoning_effort: Literal["none", "minimal", "low", "medium", "high"] | None | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: int | None | Omit = omit, + service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None | Omit = omit, + stop: str | list[str] | None | Omit = omit, + store: bool | None | Omit = omit, + stream_options: dict | None | Omit = omit, + temperature: float | None | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_logprobs: int | None | Omit = omit, + top_p: float | None | Omit = omit, + user: str | Omit = omit, + verbosity: Literal["low", "medium", "high"] | None | Omit = omit, + web_search_options: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatCompletionStreamManager: + """ + Streaming wrapper with granular event API and automatic delta accumulation. + + Unlike create(stream=True), this method requires a context manager to prevent + resource leaks. Yields detailed events including content.delta, content.done, + and provides accumulated snapshots. + + Args: + Same parameters as create() method. + + Returns: + ChatCompletionStreamManager: Context manager yielding stream events. + """ +``` + +**Usage Example:** + +```python +from openai import OpenAI + +client = OpenAI() + +# Must use within context manager +with client.chat.completions.stream( + model="gpt-4", + messages=[{"role": "user", "content": "Tell me a story"}], +) as stream: + for event in stream: + if event.type == "content.delta": + print(event.delta, flush=True, end="") + elif event.type == "content.done": + print(f"\nFinal content: {event.content}") + +# Access accumulated result after streaming +print(f"Model: {stream.response.model}") +print(f"Total tokens: {stream.response.usage.total_tokens}") +``` + +### Streaming with Helpers + +Advanced streaming with context managers for easier handling. + +```python +from openai import OpenAI + +client = OpenAI() + +# Using stream() method with context manager +with client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Count to 5"}], + stream=True +) as stream: + for chunk in stream: + print(chunk.choices[0].delta.content or "", end="") + +# Using stream context manager explicitly +stream = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Tell me a joke"}], + stream=True +) + +# Access streaming response +for chunk in stream: + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="", flush=True) + +# Stream with usage information +stream = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}], + stream=True, + stream_options={"include_usage": True} +) + +for chunk in stream: + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") + # Usage included in final chunk + if hasattr(chunk, 'usage') and chunk.usage: + print(f"\nTokens used: {chunk.usage.total_tokens}") +``` + +## Types + +```python { .api } +from typing import Literal, TypeVar, Generic +from pydantic import BaseModel +from openai.types.chat import ( + ChatCompletionToolUnionParam, + ChatCompletionToolChoiceOptionParam, + completion_create_params, +) + +# Message types +ChatCompletionMessageParam = dict[str, Any] # Union of message types + +class ChatCompletionSystemMessageParam(TypedDict): + role: Literal["system"] + content: str + name: NotRequired[str] + +class ChatCompletionUserMessageParam(TypedDict): + role: Literal["user"] + content: str | list[dict] # Text or multimodal content + name: NotRequired[str] + +class ChatCompletionAssistantMessageParam(TypedDict): + role: Literal["assistant"] + content: str | None + name: NotRequired[str] + tool_calls: NotRequired[list[dict]] + +class ChatCompletionToolMessageParam(TypedDict): + role: Literal["tool"] + content: str + tool_call_id: str + +# Response types +class ChatCompletion(BaseModel): + id: str + choices: list[Choice] + created: int + model: str + object: Literal["chat.completion"] + system_fingerprint: str | None + usage: CompletionUsage | None + +class Choice(BaseModel): + finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"] + index: int + logprobs: Logprobs | None + message: ChatCompletionMessage + +class ChatCompletionMessage(BaseModel): + content: str | None + role: Literal["assistant"] + tool_calls: list[ChatCompletionMessageToolCall] | None + function_call: FunctionCall | None # Deprecated + audio: Audio | None # When modalities includes audio + +class ChatCompletionStoreMessage(BaseModel): + """Message from a stored chat completion.""" + content: str | None + role: Literal["system", "user", "assistant", "tool"] + tool_calls: list[ChatCompletionMessageToolCall] | None + tool_call_id: str | None # For tool messages + +class ChatCompletionMessageToolCall(BaseModel): + id: str + function: Function + type: Literal["function"] + +class Function(BaseModel): + arguments: str # JSON string + name: str + +class CompletionUsage(BaseModel): + completion_tokens: int + prompt_tokens: int + total_tokens: int + completion_tokens_details: CompletionTokensDetails | None + +# Streaming types +class ChatCompletionChunk(BaseModel): + id: str + choices: list[ChunkChoice] + created: int + model: str + object: Literal["chat.completion.chunk"] + system_fingerprint: str | None + usage: CompletionUsage | None # Only in final chunk with include_usage + +class ChunkChoice(BaseModel): + delta: ChoiceDelta + finish_reason: str | None + index: int + logprobs: Logprobs | None + +class ChoiceDelta(BaseModel): + content: str | None + role: Literal["assistant"] | None + tool_calls: list[ChoiceDeltaToolCall] | None + +# Parsed completion types +ResponseFormatT = TypeVar("ResponseFormatT", bound=BaseModel) + +class ParsedChatCompletion(Generic[ResponseFormatT], ChatCompletion): + """ChatCompletion with parsed content.""" + choices: list[ParsedChoice[ResponseFormatT]] + +class ParsedChoice(Generic[ResponseFormatT], Choice): + message: ParsedChatCompletionMessage[ResponseFormatT] + +class ParsedChatCompletionMessage(Generic[ResponseFormatT], ChatCompletionMessage): + parsed: ResponseFormatT | None + tool_calls: list[ParsedFunctionToolCall] | None + +class ParsedFunctionToolCall(ChatCompletionMessageToolCall): + function: ParsedFunction + type: Literal["function"] + +class ParsedFunction(Function): + parsed_arguments: BaseModel | None + +# Deletion type +class ChatCompletionDeleted(BaseModel): + id: str + deleted: bool + object: Literal["chat.completion"] + +# Tool/function definitions +class ChatCompletionToolParam(TypedDict): + type: Literal["function"] + function: FunctionDefinition + +class FunctionDefinition(TypedDict): + name: str + description: NotRequired[str] + parameters: dict # JSON Schema + strict: NotRequired[bool] # Enable strict schema adherence + +# Response format types +class ResponseFormatText(TypedDict): + type: Literal["text"] + +class ResponseFormatJSONObject(TypedDict): + type: Literal["json_object"] + +class ResponseFormatJSONSchema(TypedDict): + type: Literal["json_schema"] + json_schema: JSONSchema + +class JSONSchema(TypedDict): + name: str + description: NotRequired[str] + schema: dict # JSON Schema object + strict: NotRequired[bool] + +# Audio types +class ChatCompletionAudioParam(TypedDict): + voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"] + format: Literal["wav", "mp3", "flac", "opus", "pcm16"] + +# Streaming options +class ChatCompletionStreamOptionsParam(TypedDict): + include_usage: NotRequired[bool] + +# Tool choice types +ChatCompletionToolChoiceOptionParam = ( + Literal["none", "auto", "required"] | dict +) + +class ToolChoiceFunction(TypedDict): + type: Literal["function"] + function: FunctionChoice + +class FunctionChoice(TypedDict): + name: str + +# Stream wrapper type +class Stream(Generic[T]): + def __iter__(self) -> Iterator[T]: ... + def __next__(self) -> T: ... + def __enter__(self) -> Stream[T]: ... + def __exit__(self, *args) -> None: ... + def close(self) -> None: ... +``` + +## Async Usage + +All chat completion methods are available in async variants through `AsyncOpenAI`: + +```python +import asyncio +from openai import AsyncOpenAI + +async def main(): + client = AsyncOpenAI() + + # Async create - returns ChatCompletion or AsyncStream[ChatCompletionChunk] + response = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] + ) + print(response.choices[0].message.content) + + # Async streaming + async for chunk in await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Tell me a story"}], + stream=True + ): + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") + + # Async parse - returns ParsedChatCompletion with structured output + from pydantic import BaseModel + + class CalendarEvent(BaseModel): + name: str + date: str + participants: list[str] + + response = await client.chat.completions.parse( + model="gpt-4o-2024-08-06", + messages=[{"role": "user", "content": "Alice and Bob are meeting on Friday"}], + response_format=CalendarEvent + ) + event = response.choices[0].message.parsed + + # Other async methods: retrieve, update, list, delete, stream + # All have the same signatures as sync versions + +asyncio.run(main()) +``` + +**Note**: AsyncOpenAI uses `AsyncStream[ChatCompletionChunk]` for streaming responses instead of `Stream[ChatCompletionChunk]`. + +## Error Handling + +```python +from openai import OpenAI, APIError, RateLimitError + +client = OpenAI() + +try: + response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello"}] + ) +except RateLimitError as e: + print(f"Rate limit hit: {e}") + # Handle rate limiting (e.g., retry with backoff) +except APIError as e: + print(f"API error: {e.status_code} - {e.message}") + # Handle other API errors +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/chatkit.md b/.tessl/tiles/tessl/pypi-openai/docs/chatkit.md new file mode 100644 index 000000000..2eec50acb --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/chatkit.md @@ -0,0 +1,404 @@ +# ChatKit (Beta) + +ChatKit provides a simplified, high-level interface for building chat applications with OpenAI models. It abstracts away common patterns and provides session and thread management out of the box. + +**Note**: ChatKit is a beta feature and the API may change. + +## Capabilities + +### Create ChatKit Session + +Create a new ChatKit session for managing a chat conversation with a specified workflow. + +```python { .api } +def create( + self, + *, + user: str, + workflow: ChatSessionWorkflowParam, + chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, + expires_after: ChatSessionExpiresAfterParam | Omit = omit, + rate_limits: ChatSessionRateLimitsParam | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatSession: + """ + Create a ChatKit session with a workflow and configuration. + + Args: + user: A free-form string that identifies your end user; ensures this Session can access other objects that have the same `user` scope. + workflow: Workflow that powers the session. Must include an `id` field (workflow identifier) and optionally `state_variables`, `tracing`, and `version`. + chatkit_configuration: Optional overrides for ChatKit runtime configuration features including automatic thread titling, file upload settings, and history management. + expires_after: Optional override for session expiration timing in seconds from creation. Defaults to 10 minutes (600 seconds). + rate_limits: Optional override for per-minute request limits. When omitted, defaults to 10 requests per minute. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ChatSession: Created session with configuration, client secret, and workflow metadata. + """ +``` + +### Cancel ChatKit Session + +Cancel an active ChatKit session and release resources. + +```python { .api } +def cancel( + self, + session_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatSession: + """ + Cancel a ChatKit session. + + Args: + session_id: The ID of the session to cancel. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ChatSession: Session with status "cancelled". + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create a ChatKit session with a workflow +session = client.beta.chatkit.sessions.create( + user="user_12345", + workflow={ + "id": "workflow_abc123", + "state_variables": {"context": "customer_support"}, + "version": "v1.0" + } +) + +print(f"Session ID: {session.id}") +print(f"Status: {session.status}") +print(f"User: {session.user}") +print(f"Expires at: {session.expires_at}") + +# Cancel session when done +cancelled = client.beta.chatkit.sessions.cancel(session.id) +print(f"Cancelled: {cancelled.status}") +``` + +### Retrieve ChatKit Thread + +Get details of a specific ChatKit thread. + +```python { .api } +def retrieve( + self, + thread_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatKitThread: + """ + Retrieve a ChatKit thread by ID. + + Args: + thread_id: The ID of the thread to retrieve. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ChatKitThread: Thread object with messages and metadata. + """ +``` + +### List ChatKit Threads + +List all ChatKit threads for the current organization. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncConversationCursorPage[ChatKitThread]: + """ + List ChatKit threads with pagination. + + Args: + after: Return threads after this thread ID (for forward pagination). + before: Return threads before this thread ID (for backward pagination). + limit: Maximum number of threads to return (default 20, max 100). + order: Sort order: "asc" (oldest first) or "desc" (newest first, default). + user: Filter threads that belong to this user identifier (defaults to null for all users). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncConversationCursorPage[ChatKitThread]: Paginated list of threads with cursor-based navigation. + """ +``` + +### Delete ChatKit Thread + +Delete a ChatKit thread and all its messages. + +```python { .api } +def delete( + self, + thread_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ThreadDeleteResponse: + """ + Delete a ChatKit thread. + + Args: + thread_id: The ID of the thread to delete. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ThreadDeleteResponse: Deletion confirmation with deleted=True. + """ +``` + +### List Thread Items + +List messages and events within a ChatKit thread. + +```python { .api } +def list_items( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncPage[ChatKitThreadItem]: + """ + List items (messages and events) in a ChatKit thread. + + Args: + thread_id: The ID of the thread. + after: Return items after this item ID. + before: Return items before this item ID. + limit: Maximum number of items to return (default 20, max 100). + order: Sort order: "asc" (chronological) or "desc" (reverse chronological). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncPage[ChatKitThreadItem]: Paginated list of thread items. + """ +``` + +## Complete Usage Example + +```python +from openai import OpenAI + +client = OpenAI() + +# Create a session with a workflow +session = client.beta.chatkit.sessions.create( + user="user_12345", + workflow={ + "id": "workflow_xyz", + "state_variables": {"mode": "qa"} + }, + rate_limits={"per_minute": 20} +) + +print(f"Session created: {session.id}") +print(f"Client secret: {session.client_secret}") + +# List all threads +threads = client.beta.chatkit.threads.list(limit=10) +for thread in threads.data: + print(f"Thread: {thread.id}, Created: {thread.created_at}") + +# Retrieve a specific thread +if threads.data: + thread = client.beta.chatkit.threads.retrieve(threads.data[0].id) + print(f"Thread metadata: {thread.metadata}") + + # List items in the thread + items = client.beta.chatkit.threads.list_items(thread.id, order="asc") + for item in items.data: + print(f"Item: {item.type} - {item.content}") + + # Delete thread when no longer needed + deleted = client.beta.chatkit.threads.delete(thread.id) + print(f"Deleted: {deleted.deleted}") + +# Cancel session +client.beta.chatkit.sessions.cancel(session.id) +``` + +## Types + +```python { .api } +from typing import Literal, Dict, Union +from pydantic import BaseModel + +# Session related types +class ChatSessionWorkflowParam: + """Workflow parameter for ChatKit session.""" + id: str # Required: Identifier for the workflow + state_variables: Dict[str, Union[str, bool, float]] | None # State variables forwarded to workflow + tracing: dict | None # Optional tracing overrides (enabled by default) + version: str | None # Specific workflow version (defaults to latest) + +class ChatSessionChatKitConfigurationParam: + """Optional ChatKit runtime configuration.""" + automatic_thread_titling: dict | None # Configuration for automatic thread title generation + file_upload: dict | None # File upload settings + history: dict | None # History management settings + +class ChatSessionExpiresAfterParam: + """Session expiration configuration.""" + # Typically a dict with duration in seconds + +class ChatSessionRateLimitsParam: + """Rate limiting configuration.""" + per_minute: int | None # Requests per minute limit + +class ChatSession(BaseModel): + """ChatKit session object.""" + id: str + """Identifier for the ChatKit session.""" + + object: Literal["chatkit.session"] + """Type discriminator that is always `chatkit.session`.""" + + chatkit_configuration: dict + """Resolved ChatKit feature configuration for the session.""" + + client_secret: str + """Ephemeral client secret that authenticates session requests.""" + + expires_at: int + """Unix timestamp (in seconds) for when the session expires.""" + + max_requests_per_1_minute: int + """Convenience copy of the per-minute request limit.""" + + rate_limits: dict + """Resolved rate limit values.""" + + status: str + """Current lifecycle state of the session (e.g., "active", "cancelled").""" + + user: str + """User identifier associated with the session.""" + + workflow: dict + """Workflow metadata for the session.""" + +class ChatKitThread(BaseModel): + """ChatKit thread object.""" + id: str + object: Literal["chatkit.thread"] + created_at: int + metadata: dict[str, str] | None + session_id: str | None + +class ThreadDeleteResponse(BaseModel): + """Thread deletion confirmation.""" + id: str + object: Literal["chatkit.thread"] + deleted: bool + +class ChatKitThreadItem(BaseModel): + """Item within a ChatKit thread (message or event).""" + id: str + object: Literal["chatkit.thread.item"] + type: Literal["message", "event"] + content: str | dict + role: Literal["user", "assistant", "system"] | None + created_at: int + metadata: dict[str, str] | None + +# Pagination +class SyncPage[T](BaseModel): + data: list[T] + object: str + first_id: str | None + last_id: str | None + has_more: bool + def __iter__(self) -> Iterator[T]: ... +``` + +## Best Practices + +1. **Session Management**: Create one session per conversation context and cancel when done to free resources. + +2. **Thread Organization**: Use separate threads for distinct conversation topics or user contexts. + +3. **Metadata**: Leverage metadata for tracking user IDs, session types, or application-specific data. + +4. **Pagination**: When listing threads or items, use pagination parameters to efficiently handle large datasets. + +5. **Error Handling**: Always wrap ChatKit calls in try-except blocks to handle API errors gracefully. + +6. **Resource Cleanup**: Always cancel sessions and delete threads when they're no longer needed. + +```python +from openai import OpenAI, APIError + +client = OpenAI() + +try: + session = client.beta.chatkit.sessions.create( + user="user_abc", + workflow={"id": "workflow_123"} + ) + + # Use session... + +finally: + # Clean up + try: + client.beta.chatkit.sessions.cancel(session.id) + except APIError: + pass # Session may already be cancelled +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/client-initialization.md b/.tessl/tiles/tessl/pypi-openai/docs/client-initialization.md new file mode 100644 index 000000000..043e66a74 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/client-initialization.md @@ -0,0 +1,740 @@ +# Client Initialization + +Initialize OpenAI and Azure OpenAI clients with API credentials, configuration options, and custom HTTP settings. The library provides both synchronous and asynchronous implementations for all clients. + +## Capabilities + +### OpenAI Client + +Creates a synchronous client for making API calls to OpenAI's services. + +```python { .api } +class OpenAI: + """ + Synchronous client for OpenAI API. + + Automatically infers credentials from environment variables: + - api_key from OPENAI_API_KEY + - organization from OPENAI_ORG_ID + - project from OPENAI_PROJECT_ID + - webhook_secret from OPENAI_WEBHOOK_SECRET + - base_url from OPENAI_BASE_URL + """ + + def __init__( + self, + *, + api_key: str | Callable[[], str] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + ) -> None: + """ + Construct a new synchronous OpenAI client instance. + + Args: + api_key: OpenAI API key. Can be a string or callable that returns a string. + If None, reads from OPENAI_API_KEY environment variable. + organization: OpenAI organization ID for multi-org accounts. + If None, reads from OPENAI_ORG_ID environment variable. + project: OpenAI project ID for project-scoped usage tracking. + If None, reads from OPENAI_PROJECT_ID environment variable. + webhook_secret: Secret for verifying webhook signatures. + If None, reads from OPENAI_WEBHOOK_SECRET environment variable. + base_url: Override the default API base URL (https://api.openai.com/v1). + If None, reads from OPENAI_BASE_URL environment variable. + websocket_base_url: Base URL for WebSocket connections (realtime API). + If None, defaults to base_url with wss:// scheme. + timeout: Request timeout in seconds. Can be a float or Timeout object. + Default is NOT_GIVEN which uses library defaults. + max_retries: Maximum number of retry attempts for failed requests. + Default is 2. + default_headers: Additional HTTP headers to include in all requests. + default_query: Additional query parameters to include in all requests. + http_client: Custom httpx.Client instance for advanced HTTP configuration. + _strict_response_validation: Enable strict API response validation. + Raises APIResponseValidationError if API returns invalid data. + + Raises: + OpenAIError: If api_key is not provided and OPENAI_API_KEY is not set. + """ +``` + +Usage example: + +```python +from openai import OpenAI + +# Basic initialization with API key +client = OpenAI(api_key="sk-...") + +# Using environment variable (OPENAI_API_KEY) +client = OpenAI() + +# Advanced configuration +client = OpenAI( + api_key="sk-...", + organization="org-...", + project="proj_...", + timeout=30.0, + max_retries=3, + default_headers={"X-Custom-Header": "value"} +) + +# Dynamic API key with callable +def get_api_key() -> str: + return retrieve_key_from_secure_storage() + +client = OpenAI(api_key=get_api_key) + +# Custom HTTP client with specific configuration +import httpx + +http_client = httpx.Client( + limits=httpx.Limits(max_keepalive_connections=10), + proxies="http://proxy.example.com:8080" +) +client = OpenAI( + api_key="sk-...", + http_client=http_client +) +``` + +### Async OpenAI Client + +Creates an asynchronous client for making non-blocking API calls to OpenAI's services. + +```python { .api } +class AsyncOpenAI: + """ + Asynchronous client for OpenAI API. + + Automatically infers credentials from environment variables: + - api_key from OPENAI_API_KEY + - organization from OPENAI_ORG_ID + - project from OPENAI_PROJECT_ID + - webhook_secret from OPENAI_WEBHOOK_SECRET + - base_url from OPENAI_BASE_URL + """ + + def __init__( + self, + *, + api_key: str | Callable[[], Awaitable[str]] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + ) -> None: + """ + Construct a new asynchronous OpenAI client instance. + + Args: + api_key: OpenAI API key. Can be a string, sync callable, or async callable. + If None, reads from OPENAI_API_KEY environment variable. + organization: OpenAI organization ID for multi-org accounts. + If None, reads from OPENAI_ORG_ID environment variable. + project: OpenAI project ID for project-scoped usage tracking. + If None, reads from OPENAI_PROJECT_ID environment variable. + webhook_secret: Secret for verifying webhook signatures. + If None, reads from OPENAI_WEBHOOK_SECRET environment variable. + base_url: Override the default API base URL (https://api.openai.com/v1). + If None, reads from OPENAI_BASE_URL environment variable. + websocket_base_url: Base URL for WebSocket connections (realtime API). + If None, defaults to base_url with wss:// scheme. + timeout: Request timeout in seconds. Can be a float or Timeout object. + Default is NOT_GIVEN which uses library defaults. + max_retries: Maximum number of retry attempts for failed requests. + Default is 2. + default_headers: Additional HTTP headers to include in all requests. + default_query: Additional query parameters to include in all requests. + http_client: Custom httpx.AsyncClient instance for advanced HTTP configuration. + _strict_response_validation: Enable strict API response validation. + Raises APIResponseValidationError if API returns invalid data. + + Raises: + OpenAIError: If api_key is not provided and OPENAI_API_KEY is not set. + """ +``` + +Usage example: + +```python +import asyncio +from openai import AsyncOpenAI + +async def main(): + # Basic initialization + client = AsyncOpenAI(api_key="sk-...") + + # Using environment variable + client = AsyncOpenAI() + + # Make async API calls + response = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] + ) + print(response.choices[0].message.content) + + # Custom async HTTP client + import httpx + + async with httpx.AsyncClient( + limits=httpx.Limits(max_keepalive_connections=10) + ) as http_client: + client = AsyncOpenAI( + api_key="sk-...", + http_client=http_client + ) + response = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] + ) + +asyncio.run(main()) +``` + +### Azure OpenAI Client + +Creates a synchronous client for Azure OpenAI Service with Azure-specific authentication and configuration. + +```python { .api } +class AzureOpenAI: + """ + Synchronous client for Azure OpenAI Service. + + Automatically infers credentials from environment variables: + - api_key from AZURE_OPENAI_API_KEY + - azure_ad_token from AZURE_OPENAI_AD_TOKEN + - azure_endpoint from AZURE_OPENAI_ENDPOINT + - api_version from OPENAI_API_VERSION + - organization from OPENAI_ORG_ID + - project from OPENAI_PROJECT_ID + """ + + def __init__( + self, + *, + api_version: str | None = None, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_key: str | Callable[[], str] | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: Callable[[], str] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx.URL | None = None, + base_url: str | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + _strict_response_validation: bool = False, + ) -> None: + """ + Construct a new synchronous Azure OpenAI client instance. + + Authentication is mutually exclusive - provide only ONE of: + - api_key + - azure_ad_token + - azure_ad_token_provider + + Args: + api_version: Azure OpenAI API version (e.g., "2024-02-15-preview"). + If None, reads from OPENAI_API_VERSION environment variable. + azure_endpoint: Azure resource endpoint (e.g., "https://example.azure.openai.com/"). + If None, reads from AZURE_OPENAI_ENDPOINT environment variable. + azure_deployment: Model deployment name. When provided with azure_endpoint, + sets base URL to include /deployments/{azure_deployment}. + Not supported with Assistants APIs. + api_key: Azure OpenAI API key. + If None, reads from AZURE_OPENAI_API_KEY environment variable. + azure_ad_token: Azure Active Directory (Entra ID) access token. + If None, reads from AZURE_OPENAI_AD_TOKEN environment variable. + azure_ad_token_provider: Function returning Azure AD token, invoked per request. + Useful for automatic token refresh. + organization: OpenAI organization ID (optional for Azure). + If None, reads from OPENAI_ORG_ID environment variable. + project: OpenAI project ID (optional for Azure). + If None, reads from OPENAI_PROJECT_ID environment variable. + webhook_secret: Secret for verifying webhook signatures. + websocket_base_url: Base URL for WebSocket connections. + base_url: Override the base URL (alternative to azure_endpoint). + timeout: Request timeout in seconds. + Default is NOT_GIVEN which uses library defaults. + max_retries: Maximum number of retry attempts for failed requests. + Default is 2. + default_headers: Additional HTTP headers to include in all requests. + default_query: Additional query parameters to include in all requests. + http_client: Custom httpx.Client instance for advanced HTTP configuration. + _strict_response_validation: Enable strict API response validation. + + Raises: + OpenAIError: If no authentication credentials are provided. + MutuallyExclusiveAuthError: If multiple authentication methods are provided. + """ +``` + +Usage example: + +```python +from openai import AzureOpenAI + +# Using API key authentication +client = AzureOpenAI( + api_key="your-azure-api-key", + azure_endpoint="https://your-resource.azure.openai.com/", + api_version="2024-02-15-preview" +) + +# Using environment variables +# Set AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, OPENAI_API_VERSION +client = AzureOpenAI() + +# With deployment name (for model-specific endpoints) +client = AzureOpenAI( + api_key="your-azure-api-key", + azure_endpoint="https://your-resource.azure.openai.com/", + azure_deployment="gpt-4-deployment", + api_version="2024-02-15-preview" +) + +# Using Azure AD (Entra ID) authentication +client = AzureOpenAI( + azure_ad_token="your-azure-ad-token", + azure_endpoint="https://your-resource.azure.openai.com/", + api_version="2024-02-15-preview" +) + +# Using Azure AD with automatic token refresh +def get_azure_ad_token() -> str: + # Implement token retrieval/refresh logic + from azure.identity import DefaultAzureCredential + credential = DefaultAzureCredential() + token = credential.get_token("https://cognitiveservices.azure.com/.default") + return token.token + +client = AzureOpenAI( + azure_ad_token_provider=get_azure_ad_token, + azure_endpoint="https://your-resource.azure.openai.com/", + api_version="2024-02-15-preview" +) + +# Make API calls (same interface as OpenAI) +response = client.chat.completions.create( + model="gpt-4", # Uses deployment if azure_deployment was set + messages=[{"role": "user", "content": "Hello!"}] +) +``` + +### Async Azure OpenAI Client + +Creates an asynchronous client for Azure OpenAI Service. + +```python { .api } +class AsyncAzureOpenAI: + """ + Asynchronous client for Azure OpenAI Service. + + Automatically infers credentials from environment variables: + - api_key from AZURE_OPENAI_API_KEY + - azure_ad_token from AZURE_OPENAI_AD_TOKEN + - azure_endpoint from AZURE_OPENAI_ENDPOINT + - api_version from OPENAI_API_VERSION + - organization from OPENAI_ORG_ID + - project from OPENAI_PROJECT_ID + """ + + def __init__( + self, + *, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_version: str | None = None, + api_key: str | Callable[[], Awaitable[str]] | None = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: Callable[[], str | Awaitable[str]] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + _strict_response_validation: bool = False, + ) -> None: + """ + Construct a new asynchronous Azure OpenAI client instance. + + Authentication is mutually exclusive - provide only ONE of: + - api_key + - azure_ad_token + - azure_ad_token_provider + + Args: + api_version: Azure OpenAI API version (e.g., "2024-02-15-preview"). + If None, reads from OPENAI_API_VERSION environment variable. + azure_endpoint: Azure resource endpoint (e.g., "https://example.azure.openai.com/"). + If None, reads from AZURE_OPENAI_ENDPOINT environment variable. + azure_deployment: Model deployment name. When provided with azure_endpoint, + sets base URL to include /deployments/{azure_deployment}. + Not supported with Assistants APIs. + api_key: Azure OpenAI API key. Can be a string, sync callable, or async callable. + If None, reads from AZURE_OPENAI_API_KEY environment variable. + azure_ad_token: Azure Active Directory (Entra ID) access token. + If None, reads from AZURE_OPENAI_AD_TOKEN environment variable. + azure_ad_token_provider: Function returning Azure AD token (sync or async), + invoked per request. Useful for automatic token refresh. + organization: OpenAI organization ID (optional for Azure). + If None, reads from OPENAI_ORG_ID environment variable. + project: OpenAI project ID (optional for Azure). + If None, reads from OPENAI_PROJECT_ID environment variable. + webhook_secret: Secret for verifying webhook signatures. + websocket_base_url: Base URL for WebSocket connections. + base_url: Override the base URL (alternative to azure_endpoint). + timeout: Request timeout in seconds. + Default is NOT_GIVEN which uses library defaults. + max_retries: Maximum number of retry attempts for failed requests. + Default is 2. + default_headers: Additional HTTP headers to include in all requests. + default_query: Additional query parameters to include in all requests. + http_client: Custom httpx.AsyncClient instance for advanced HTTP configuration. + _strict_response_validation: Enable strict API response validation. + + Raises: + OpenAIError: If no authentication credentials are provided. + MutuallyExclusiveAuthError: If multiple authentication methods are provided. + """ +``` + +Usage example: + +```python +import asyncio +from openai import AsyncAzureOpenAI + +async def main(): + # Basic async Azure client + client = AsyncAzureOpenAI( + api_key="your-azure-api-key", + azure_endpoint="https://your-resource.azure.openai.com/", + api_version="2024-02-15-preview" + ) + + # Async Azure AD token provider + async def get_azure_ad_token() -> str: + # Implement async token retrieval + from azure.identity.aio import DefaultAzureCredential + credential = DefaultAzureCredential() + token = await credential.get_token("https://cognitiveservices.azure.com/.default") + return token.token + + client = AsyncAzureOpenAI( + azure_ad_token_provider=get_azure_ad_token, + azure_endpoint="https://your-resource.azure.openai.com/", + api_version="2024-02-15-preview" + ) + + # Make async API calls + response = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] + ) + print(response.choices[0].message.content) + +asyncio.run(main()) +``` + +## Types + +```python { .api } +from typing import Callable, Mapping, Awaitable +import httpx + +# Timeout configuration +Timeout = float | httpx.Timeout + +# Sentinel for omitted parameters +class NotGiven: + """Sentinel value indicating a parameter was not provided.""" + +NOT_GIVEN: NotGiven + +# Azure AD token providers +AzureADTokenProvider = Callable[[], str] +AsyncAzureADTokenProvider = Callable[[], str | Awaitable[str]] +``` + +## Client Utility Methods + +All clients provide utility methods for creating client copies with modified configuration and accessing raw HTTP responses. + +### Copy Client with Modified Options + +Create a new client instance reusing the same options with optional overrides. + +```python { .api } +def copy( + self, + *, + api_key: str | Callable[[], str] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + websocket_base_url: str | httpx.URL | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.Client | None = None, + max_retries: int | Omit = omit, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, +) -> OpenAI: + """ + Create a new client instance re-using the same options with optional overriding. + + Args: + api_key: Override API key + organization: Override organization + project: Override project + webhook_secret: Override webhook secret + websocket_base_url: Override WebSocket base URL + base_url: Override base URL + timeout: Override timeout + http_client: Override HTTP client + max_retries: Override max retries + default_headers: Merge these headers with existing default headers + set_default_headers: Replace all default headers with these + default_query: Merge these query params with existing defaults + set_default_query: Replace all default query params with these + + Returns: + New client instance with merged configuration. + + Note: + - default_headers and set_default_headers are mutually exclusive + - default_query and set_default_query are mutually exclusive + """ + +# Alias for nicer inline usage +with_options = copy +``` + +Usage example: + +```python +from openai import OpenAI + +# Create base client +client = OpenAI(api_key="sk-...", timeout=30) + +# Create a copy with longer timeout for specific operations +slow_client = client.copy(timeout=120) + +# Or use the with_options alias for inline usage +response = client.with_options(timeout=60).chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Complex query..."}] +) + +# Create a copy with additional headers +custom_client = client.copy( + default_headers={"X-Request-ID": "abc123"} +) + +# Replace all headers +new_client = client.copy( + set_default_headers={"X-Custom": "value"} +) +``` + +### Access Raw HTTP Responses + +Access the underlying `httpx.Response` object for any API call. + +```python { .api } +@property +def with_raw_response(self) -> OpenAIWithRawResponse: + """ + Property that returns a client wrapper providing access to raw HTTP responses. + + All API methods on this wrapper return a response object with: + - parsed: The normally returned parsed response object + - http_response: The raw httpx.Response object + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Access raw HTTP response +response = client.with_raw_response.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] +) + +# Access parsed response +completion = response.parsed +print(completion.choices[0].message.content) + +# Access raw HTTP response details +http_response = response.http_response +print(f"Status: {http_response.status_code}") +print(f"Headers: {dict(http_response.headers)}") +print(f"Request ID: {http_response.headers.get('x-request-id')}") +``` + +### Stream HTTP Responses + +Stream responses without loading them into memory, useful for large responses like file downloads. + +```python { .api } +@property +def with_streaming_response(self) -> OpenAIWithStreamedResponse: + """ + Property that returns a client wrapper for streaming HTTP responses. + + Instead of loading the entire response into memory, returns a response + object that can be iterated over in chunks. + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Stream file content without loading into memory +response = client.with_streaming_response.files.content("file-abc123") + +# Stream chunks +for chunk in response.iter_bytes(chunk_size=8192): + process_chunk(chunk) + +# Or write directly to file +with open("output.txt", "wb") as f: + for chunk in response.iter_bytes(): + f.write(chunk) + +# Also works with other endpoints +response = client.with_streaming_response.audio.speech.create( + model="tts-1", + voice="alloy", + input="Hello world" +) + +with open("speech.mp3", "wb") as f: + for chunk in response.iter_bytes(): + f.write(chunk) +``` + +Async clients have the same methods: + +```python +from openai import AsyncOpenAI + +client = AsyncOpenAI() + +# Raw response (async) +response = await client.with_raw_response.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] +) + +# Streaming response (async) +response = await client.with_streaming_response.files.content("file-abc123") +async for chunk in response.iter_bytes(): + process_chunk(chunk) + +# Copy/with_options (async) +slow_client = client.copy(timeout=120) +``` + +## Client Properties + +All clients (OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI) expose the following resource properties for accessing API endpoints: + +```python { .api } +# Core resources +client.chat # Chat completions +client.completions # Legacy text completions +client.embeddings # Vector embeddings +client.audio # Audio transcription, translation, TTS +client.images # Image generation +client.videos # Video generation +client.files # File management +client.models # Model information +client.moderations # Content moderation +client.fine_tuning # Fine-tuning jobs +client.batches # Batch processing +client.vector_stores # Vector stores +client.uploads # Multipart uploads +client.responses # Responses API +client.realtime # Realtime API +client.webhooks # Webhook verification +client.beta # Beta features (Assistants, Threads, etc.) +client.conversations # Conversations API +client.evals # Evaluations +client.containers # Containers +``` + +## Error Handling + +All client initialization errors inherit from OpenAIError: + +```python { .api } +class OpenAIError(Exception): + """Base exception for all OpenAI errors.""" + +class MutuallyExclusiveAuthError(OpenAIError): + """Raised when multiple authentication methods are provided to Azure clients.""" +``` + +Common initialization errors: + +```python +from openai import OpenAI, OpenAIError, AzureOpenAI, MutuallyExclusiveAuthError + +# Handle missing API key +try: + client = OpenAI() # No api_key and OPENAI_API_KEY not set +except OpenAIError as e: + print(f"Missing API key: {e}") + +# Handle Azure authentication conflicts +try: + client = AzureOpenAI( + api_key="key", + azure_ad_token="token" # Can't provide both + ) +except MutuallyExclusiveAuthError as e: + print(f"Auth conflict: {e}") +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/completions.md b/.tessl/tiles/tessl/pypi-openai/docs/completions.md new file mode 100644 index 000000000..3b334d8ba --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/completions.md @@ -0,0 +1,292 @@ +# Text Completions + +Generate text completions using legacy completion models. This API is superseded by Chat Completions for most use cases, but remains available for compatibility with specific completion-optimized models. + +## Capabilities + +### Create Completion + +Generate text completion for a given prompt. + +```python { .api } +def create( + self, + *, + model: str, + prompt: str | list[str] | list[int] | list[list[int]], + best_of: int | Omit = omit, + echo: bool | Omit = omit, + frequency_penalty: float | Omit = omit, + logit_bias: dict[str, int] | Omit = omit, + logprobs: int | Omit = omit, + max_tokens: int | Omit = omit, + n: int | Omit = omit, + presence_penalty: float | Omit = omit, + seed: int | Omit = omit, + stop: str | list[str] | Omit = omit, + stream: bool | Omit = omit, + stream_options: dict | None | Omit = omit, + suffix: str | Omit = omit, + temperature: float | Omit = omit, + top_p: float | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Completion | Stream[Completion]: + """ + Generate text completion for a prompt. + + NOTE: Most use cases are better served by Chat Completions API. + This endpoint is primarily for completion-specific models like gpt-3.5-turbo-instruct. + + Args: + model: Model ID. Supported models: + - "gpt-3.5-turbo-instruct": Instruction-tuned model optimized for completions + - "davinci-002": Legacy Davinci model + - "babbage-002": Legacy Babbage model + + prompt: Text prompt(s) to complete. Can be: + - Single string: "Once upon a time" + - List of strings: ["Story 1", "Story 2"] + - Token array: [123, 456, 789] + - List of token arrays: [[123, 456], [789, 012]] + + best_of: Generates best_of completions server-side, returns best one. + Must be greater than n. Used with temperature for better quality. + Cannot be used with stream. + + echo: If true, echoes the prompt in addition to completion. + + frequency_penalty: Number between -2.0 and 2.0. Penalizes tokens based on + their frequency in the text so far. Default 0. + + logit_bias: Modify token probabilities. Maps token IDs to bias values + from -100 to 100. + + logprobs: Include log probabilities of the most likely tokens. + Returns logprobs number of most likely tokens per position. + Maximum value is 5. + + max_tokens: Maximum tokens to generate. Default 16. + + n: Number of completions to generate. Default 1. + + presence_penalty: Number between -2.0 and 2.0. Penalizes tokens based on + whether they appear in the text so far. Default 0. + + seed: For deterministic sampling (Beta). Same seed + parameters should + return same result. Not guaranteed. + + stop: Up to 4 sequences where generation stops. Can be string or list. + + stream: If true, returns SSE stream of partial completions. + + stream_options: Streaming configuration. Accepts dict with: + - "include_usage": bool - If true, includes token usage in final chunk + - "include_obfuscation": bool - If true (default), adds random characters + to obfuscation field on streaming delta events to normalize payload sizes + as mitigation for side-channel attacks. Set to false to optimize bandwidth. + + suffix: Text that comes after the completion. Useful for inserting text. + + temperature: Sampling temperature between 0 and 2. Higher values make + output more random. Default 1. Alter this or top_p, not both. + + top_p: Nucleus sampling parameter between 0 and 1. Default 1. + Alter this or temperature, not both. + + user: Unique end-user identifier for abuse monitoring. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Completion: If stream=False (default), returns complete response. + Stream[Completion]: If stream=True, returns streaming response. + + Raises: + BadRequestError: Invalid parameters + AuthenticationError: Invalid API key + RateLimitError: Rate limit exceeded + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic completion +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="Once upon a time", + max_tokens=50 +) + +print(response.choices[0].text) + +# Multiple prompts +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt=[ + "The capital of France is", + "The largest ocean on Earth is" + ], + max_tokens=10 +) + +for i, choice in enumerate(response.choices): + print(f"Completion {i + 1}: {choice.text}") + +# With suffix for text insertion +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="def calculate_sum(a, b):\n \"\"\"", + suffix="\n return a + b", + max_tokens=50 +) + +print(response.choices[0].text) + +# With logprobs +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="The weather today is", + max_tokens=5, + logprobs=2, + echo=True +) + +# Access log probabilities +for token, logprob in zip(response.choices[0].logprobs.tokens, + response.choices[0].logprobs.token_logprobs): + print(f"Token: {token}, Log Prob: {logprob}") + +# Streaming completion +stream = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="Write a short poem about coding:", + max_tokens=100, + stream=True +) + +for chunk in stream: + if chunk.choices[0].text: + print(chunk.choices[0].text, end="", flush=True) + +# With best_of for higher quality +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="Explain quantum computing in simple terms:", + max_tokens=100, + best_of=3, + n=1, + temperature=0.8 +) + +# With stop sequences +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="List three colors:\n1.", + max_tokens=50, + stop=["\n4.", "\n\n"] +) + +# Deterministic completion with seed +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="Generate a random number:", + seed=42, + max_tokens=10 +) +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Completion(BaseModel): + """Completion response.""" + id: str + choices: list[CompletionChoice] + created: int + model: str + object: Literal["text_completion"] + system_fingerprint: str | None + usage: CompletionUsage | None + +class CompletionChoice(BaseModel): + """Single completion choice.""" + finish_reason: Literal["stop", "length", "content_filter"] + index: int + logprobs: Logprobs | None + text: str + +class Logprobs(BaseModel): + """Log probability information.""" + text_offset: list[int] + token_logprobs: list[float | None] + tokens: list[str] + top_logprobs: list[dict[str, float]] | None + +class CompletionUsage(BaseModel): + """Token usage statistics.""" + completion_tokens: int + prompt_tokens: int + total_tokens: int + +# Stream wrapper +class Stream(Generic[T]): + def __iter__(self) -> Iterator[T]: ... + def __next__(self) -> T: ... + def __enter__(self) -> Stream[T]: ... + def __exit__(self, *args) -> None: ... +``` + +## Migration to Chat Completions + +For most use cases, Chat Completions API is recommended: + +```python +# Legacy Completions +response = client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="Translate to French: Hello" +) + +# Equivalent Chat Completion +response = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "user", "content": "Translate to French: Hello"} + ] +) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def complete_text(): + client = AsyncOpenAI() + + response = await client.completions.create( + model="gpt-3.5-turbo-instruct", + prompt="Once upon a time", + max_tokens=50 + ) + + return response.choices[0].text + +text = asyncio.run(complete_text()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/containers.md b/.tessl/tiles/tessl/pypi-openai/docs/containers.md new file mode 100644 index 000000000..cbe63e970 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/containers.md @@ -0,0 +1,641 @@ +# Containers + +Create and manage isolated file storage containers for organizing and sharing files. Containers provide a way to group related files together with configurable expiration policies. + +## Capabilities + +### Create Container + +Create a new container for file storage. + +```python { .api } +def create( + self, + *, + name: str, + expires_after: dict | Omit = omit, + file_ids: list[str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Container: + """ + Create a container. + + Args: + name: Name of the container to create. Used for identification. + + expires_after: Container expiration time in seconds relative to anchor time. + Example: {"anchor": "created_at", "days": 7} expires 7 days after creation. + Default: containers persist until manually deleted. + + file_ids: IDs of files to copy into the container at creation. + Files are copied, not moved - originals remain accessible. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Container: Created container object with unique ID. + + Notes: + - Containers provide isolated file storage + - Files are copied into containers, not moved + - Expired containers are automatically cleaned up + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create empty container +container = client.containers.create( + name="project-resources" +) +print(f"Created container: {container.id}") + +# Create with files +container = client.containers.create( + name="training-data", + file_ids=["file-abc123", "file-def456"] +) + +# Create with expiration +container = client.containers.create( + name="temp-files", + expires_after={ + "anchor": "created_at", + "days": 1 + } +) + +# Create for specific project +container = client.containers.create( + name="customer-support-logs", + file_ids=["file-log1", "file-log2", "file-log3"], + expires_after={ + "anchor": "created_at", + "days": 30 + } +) +``` + +### Retrieve Container + +Get a container by its ID. + +```python { .api } +def retrieve( + self, + container_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Container: + """ + Retrieve a container. + + Args: + container_id: ID of the container to retrieve. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Container: Container object with metadata and file information. + """ +``` + +### List Containers + +List all containers with pagination support. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Container]: + """ + List containers. + + Args: + after: Cursor for pagination. Object ID defining your place in the list. + For instance, if you receive 100 objects ending with obj_foo, + use after=obj_foo to fetch the next page. + + limit: Maximum number of objects to return. Range: 1-100. Default: 20. + + order: Sort order by created_at timestamp. + - "asc": Ascending (oldest first) + - "desc": Descending (newest first, default) + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[Container]: Paginated list of containers. + Supports iteration: for container in client.containers.list(): ... + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# List all containers +for container in client.containers.list(): + print(f"{container.name}: {container.id}") + +# List with pagination +page = client.containers.list(limit=10) +for container in page: + print(f"{container.name} - Created: {container.created_at}") + +# Get next page +if page.has_more: + next_page = client.containers.list( + limit=10, + after=page.data[-1].id + ) + +# List in ascending order (oldest first) +for container in client.containers.list(order="asc"): + print(container.name) +``` + +### Delete Container + +Delete a container. + +```python { .api } +def delete( + self, + container_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> None: + """ + Delete a container. + + Args: + container_id: ID of the container to delete. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + None: No content returned on successful deletion. + + Notes: + - Deletion is permanent and cannot be undone + - Files within the container are not deleted + - Original files remain accessible outside the container + """ +``` + +### Container Files + +Manage files within a container. + +```python { .api } +# Access via client.containers.files + +def create( + self, + container_id: str, + *, + file: FileTypes | Omit = omit, + file_id: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FileCreateResponse: + """ + Add a file to a container. + + Args: + container_id: ID of the container. + + file: File content to upload directly. Provide either this OR file_id. + + file_id: ID of an existing file to add. File is copied into container. + Provide either this OR file. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FileCreateResponse: File reference within the container. + + Notes: + - Provide either `file` (for direct upload) OR `file_id` (for reference) + - When using file_id, file is copied, not moved + - Original file remains accessible + - Same file can be in multiple containers + """ + +def retrieve( + self, + container_id: str, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ContainerFile: + """ + Retrieve a file from a container. + + Args: + container_id: ID of the container. + file_id: ID of the file to retrieve. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ContainerFile: File information and metadata. + """ + +def list( + self, + container_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[ContainerFile]: + """ + List files in a container. + + Args: + container_id: ID of the container. + + after: Cursor for pagination. + + limit: Maximum number of files to return. Range: 1-100. Default: 20. + + order: Sort order by created_at timestamp ("asc" or "desc"). + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[ContainerFile]: Paginated list of files. + """ + +def delete( + self, + container_id: str, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> None: + """ + Remove a file from a container. + + Args: + container_id: ID of the container. + file_id: ID of the file to remove. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + None: No content returned on successful removal. + + Notes: + - Removes file from container only + - Original file is not deleted + - File remains accessible outside container + """ +``` + +### Container File Content + +Retrieve the binary content of a file stored in a container. + +```python { .api } +# Access via client.containers.files.content + +def retrieve( + self, + file_id: str, + *, + container_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> HttpxBinaryResponseContent: + """ + Retrieve the binary content of a container file. + + Args: + file_id: ID of the file to retrieve content from. + + container_id: ID of the container containing the file. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + HttpxBinaryResponseContent: Binary content of the file. + Can be written to disk or processed in memory. + + Notes: + - Returns raw binary data + - Suitable for downloading file contents + - Use .read() or .iter_bytes() to access content + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Retrieve file content from container +content = client.containers.files.content.retrieve( + container_id="container-abc123", + file_id="file-def456" +) + +# Save to disk +with open("downloaded_file.pdf", "wb") as f: + f.write(content.read()) + +# Or process in chunks +for chunk in content.iter_bytes(chunk_size=8192): + # Process chunk + pass +``` + +Complete workflow example: + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Upload some files first +file1 = client.files.create( + file=open("document1.pdf", "rb"), + purpose="assistants" +) + +file2 = client.files.create( + file=open("document2.pdf", "rb"), + purpose="assistants" +) + +# 2. Create container with one file +container = client.containers.create( + name="project-docs", + file_ids=[file1.id], + expires_after={ + "anchor": "created_at", + "days": 7 + } +) + +print(f"Created container: {container.id}") + +# 3. Add another file to container +client.containers.files.create( + container_id=container.id, + file_id=file2.id +) + +# 4. List all files in container +print("Files in container:") +for file in client.containers.files.list(container_id=container.id): + print(f" - {file.id}") + +# 5. Retrieve specific file from container +file_info = client.containers.files.retrieve( + container_id=container.id, + file_id=file1.id +) +print(f"File info: {file_info}") + +# 6. Remove a file from container +client.containers.files.delete( + container_id=container.id, + file_id=file1.id +) + +# 7. Verify removal +remaining_files = client.containers.files.list(container_id=container.id) +print(f"Remaining files: {len(remaining_files.data)}") + +# 8. List all containers +print("\nAll containers:") +for c in client.containers.list(): + print(f" {c.name} ({c.id})") + +# 9. Clean up - delete container +client.containers.delete(container_id=container.id) + +# Note: Original files still exist +retrieved_file = client.files.retrieve(file_id=file1.id) +print(f"Original file still exists: {retrieved_file.id}") +``` + +Organizing files by project: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create containers for different projects +ml_container = client.containers.create( + name="machine-learning-project" +) + +web_container = client.containers.create( + name="web-development-project" +) + +# Upload and organize files +ml_file = client.files.create( + file=open("model_training.py", "rb"), + purpose="assistants" +) + +web_file = client.files.create( + file=open("api_docs.md", "rb"), + purpose="assistants" +) + +# Add files to appropriate containers +client.containers.files.create( + container_id=ml_container.id, + file_id=ml_file.id +) + +client.containers.files.create( + container_id=web_container.id, + file_id=web_file.id +) + +# Later: access files by project +print("ML Project Files:") +for file in client.containers.files.list(container_id=ml_container.id): + print(f" - {file.id}") + +print("Web Project Files:") +for file in client.containers.files.list(container_id=web_container.id): + print(f" - {file.id}") +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def manage_containers(): + client = AsyncOpenAI() + + # Create container + container = await client.containers.create( + name="async-project", + expires_after={"anchor": "created_at", "days": 1} + ) + + # Add file + await client.containers.files.create( + container_id=container.id, + file_id="file-abc123" + ) + + # List files + async for file in await client.containers.files.list( + container_id=container.id + ): + print(f"File: {file.id}") + + # Delete container + await client.containers.delete(container_id=container.id) + +asyncio.run(manage_containers()) +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Container(BaseModel): + """Container object for file organization.""" + id: str + created_at: int + name: str + object: Literal["container"] + expires_at: int | None # Present if expiration configured + file_count: int | None # Number of files in container + +class ContainerFile(BaseModel): + """File reference within a container.""" + id: str + container_id: str + file_id: str + created_at: int + object: Literal["container.file"] + +class SyncCursorPage[T]: + """Cursor-based pagination.""" + data: list[T] + has_more: bool + def __iter__(self) -> Iterator[T]: ... + +class Omit: + """Sentinel value for omitted parameters.""" + +class HttpxBinaryResponseContent: + """Binary response content from HTTP requests.""" + def read(self) -> bytes: ... + def iter_bytes(self, chunk_size: int = ...) -> Iterator[bytes]: ... +``` + +## Access Pattern + +```python +# Synchronous +from openai import OpenAI +client = OpenAI() +client.containers.create(...) +client.containers.retrieve(...) +client.containers.list(...) +client.containers.delete(...) +client.containers.files.create(...) +client.containers.files.retrieve(...) +client.containers.files.list(...) +client.containers.files.delete(...) +client.containers.files.content.retrieve(...) + +# Asynchronous +from openai import AsyncOpenAI +client = AsyncOpenAI() +await client.containers.create(...) +await client.containers.retrieve(...) +await client.containers.list(...) +await client.containers.delete(...) +await client.containers.files.create(...) +await client.containers.files.retrieve(...) +await client.containers.files.list(...) +await client.containers.files.delete(...) +await client.containers.files.content.retrieve(...) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/conversations.md b/.tessl/tiles/tessl/pypi-openai/docs/conversations.md new file mode 100644 index 000000000..c9d14e156 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/conversations.md @@ -0,0 +1,599 @@ +# Conversations + +Create and manage conversations for structured multi-turn interactions. Conversations provide a lightweight alternative to Threads for managing conversational context with items that can include user inputs, system messages, and function outputs. + +## Capabilities + +### Create Conversation + +Create a new conversation with optional initial items. + +```python { .api } +def create( + self, + *, + items: Iterable[dict] | None | Omit = omit, + metadata: dict[str, str] | None | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: + """ + Create a conversation. + + Args: + items: Initial items to include in the conversation context. + You may add up to 20 items at a time. Each item can be: + - Message: {"type": "message", "role": "user", "content": "..."} + - System: {"type": "message", "role": "system", "content": "..."} + - Function output: {"type": "function_call_output", "call_id": "...", "output": "..."} + + metadata: Up to 16 key-value pairs for storing additional information. + Keys max 64 characters, values max 512 characters. + Useful for storing user IDs, session info, etc. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Conversation: Created conversation object with unique ID. + + Notes: + - Conversations are lightweight compared to Threads + - No automatic message persistence - items must be explicitly managed + - Use for scenarios requiring explicit context control + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create empty conversation +conversation = client.conversations.create() +print(f"Created conversation: {conversation.id}") + +# Create with initial items +conversation = client.conversations.create( + items=[ + { + "type": "message", + "role": "system", + "content": "You are a helpful coding assistant." + }, + { + "type": "message", + "role": "user", + "content": "How do I reverse a string in Python?" + } + ], + metadata={ + "user_id": "user_123", + "session": "session_abc" + } +) + +# Create with metadata +conversation = client.conversations.create( + metadata={ + "purpose": "customer_support", + "language": "en", + "priority": "high" + } +) +``` + +### Retrieve Conversation + +Get a conversation by its ID. + +```python { .api } +def retrieve( + self, + conversation_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: + """ + Get a conversation. + + Args: + conversation_id: ID of the conversation to retrieve. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Conversation: Conversation object with current metadata and items. + """ +``` + +### Update Conversation + +Update conversation metadata. + +```python { .api } +def update( + self, + conversation_id: str, + *, + metadata: dict[str, str] | None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: + """ + Update a conversation. + + Args: + conversation_id: ID of the conversation to update. + + metadata: New metadata dictionary. Replaces all existing metadata. + Up to 16 key-value pairs, keys max 64 chars, values max 512 chars. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Conversation: Updated conversation object. + + Notes: + - Metadata is completely replaced, not merged + - To preserve existing metadata, retrieve first then update + """ +``` + +### Delete Conversation + +Delete a conversation. + +```python { .api } +def delete( + self, + conversation_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationDeleted: + """ + Delete a conversation. + + Args: + conversation_id: ID of the conversation to delete. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ConversationDeleted: Deletion confirmation with ID and status. + + Notes: + - Items in the conversation will not be deleted + - Deletion is permanent and cannot be undone + - Items remain accessible if you have their IDs + """ +``` + +### Conversation Items + +Manage items within a conversation. + +```python { .api } +# Access via client.conversations.items + +def create( + self, + conversation_id: str, + *, + items: Iterable[dict], + include: list[Literal[ + "web_search_call.action.sources", + "code_interpreter_call.outputs", + "computer_call_output.output.image_url", + "file_search_call.results", + "message.input_image.image_url", + "message.output_text.logprobs", + "reasoning.encrypted_content" + ]] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationItemList: + """ + Create items in a conversation. + + Args: + conversation_id: ID of the conversation. + + items: The items to add to the conversation. You may add up to 20 items at a time. + Each item is a dict with one of these structures: + - Message: {"type": "message", "role": "user"|"system"|"developer", "content": [...]} + - Function call output: {"type": "function_call_output", "call_id": "...", "output": "..."} + - Computer call output: {"type": "computer_call_output", "call_id": "...", "output": {...}} + - Shell call output: {"type": "shell_call_output", "call_id": "...", "output": [...]} + - And many other item types (see ResponseInputItemParam for full list) + + include: Additional fields to include in the response. Options: + - "web_search_call.action.sources": Include web search sources + - "code_interpreter_call.outputs": Include code execution outputs + - "computer_call_output.output.image_url": Include computer call images + - "file_search_call.results": Include file search results + - "message.input_image.image_url": Include input message images + - "message.output_text.logprobs": Include logprobs with assistant messages + - "reasoning.encrypted_content": Include encrypted reasoning tokens + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ConversationItemList: List of created items with IDs and timestamps. + """ + +def retrieve( + self, + item_id: str, + *, + conversation_id: str, + include: list[Literal[ + "web_search_call.action.sources", + "code_interpreter_call.outputs", + "computer_call_output.output.image_url", + "file_search_call.results", + "message.input_image.image_url", + "message.output_text.logprobs", + "reasoning.encrypted_content" + ]] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationItem: + """ + Retrieve a specific item from a conversation. + + Args: + item_id: ID of the item to retrieve. + conversation_id: ID of the conversation. + + include: Additional fields to include in the response. See create() for available options. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ConversationItem: Item with full content and metadata. + """ + +def delete( + self, + item_id: str, + *, + conversation_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: + """ + Delete an item from a conversation. + + Args: + item_id: ID of the item to delete. + conversation_id: ID of the conversation. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Conversation: Updated conversation after item deletion. + + Notes: + - Deletion is permanent + - Returns the conversation object (not a deletion confirmation) + """ + +def list( + self, + conversation_id: str, + *, + after: str | Omit = omit, + include: list[Literal[ + "web_search_call.action.sources", + "code_interpreter_call.outputs", + "computer_call_output.output.image_url", + "file_search_call.results", + "message.input_image.image_url", + "message.output_text.logprobs", + "reasoning.encrypted_content" + ]] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncConversationCursorPage[ConversationItem]: + """ + List items in a conversation. + + Args: + conversation_id: ID of the conversation. + + after: Cursor for pagination. Return items after this ID. + + include: Additional fields to include in the response. See create() for available options. + + limit: Maximum number of items to return (1-100). Default 20. + + order: Sort order by creation time: + - "asc": Ascending (oldest first) + - "desc": Descending (newest first, default) + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncConversationCursorPage[ConversationItem]: Paginated list of items. + """ +``` + +Complete workflow example: + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Create conversation +conversation = client.conversations.create( + metadata={"user_id": "user_123"} +) + +# 2. Add system and user messages (batch create) +items_response = client.conversations.items.create( + conversation_id=conversation.id, + items=[ + { + "type": "message", + "role": "system", + "content": [{"type": "text", "text": "You are a helpful Python tutor."}] + }, + { + "type": "message", + "role": "user", + "content": [{"type": "text", "text": "How do I read a file in Python?"}] + } + ] +) + +system_item = items_response.data[0] +user_item = items_response.data[1] + +# 4. List all items +items = client.conversations.items.list( + conversation_id=conversation.id, + order="asc" +) + +for item in items: + print(f"{item.role}: {item.content}") + +# 5. Add assistant response +assistant_response = client.conversations.items.create( + conversation_id=conversation.id, + items=[ + { + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "You can read a file using: with open('file.txt', 'r') as f: content = f.read()"}] + } + ] +) +assistant_item = assistant_response.data[0] + +# 6. Update conversation metadata +client.conversations.update( + conversation_id=conversation.id, + metadata={ + "user_id": "user_123", + "status": "resolved", + "topic": "file_io" + } +) + +# 7. Get updated conversation +updated = client.conversations.retrieve(conversation_id=conversation.id) +print(f"Metadata: {updated.metadata}") + +# 8. Delete specific item +client.conversations.items.delete( + conversation_id=conversation.id, + item_id=user_item.id +) + +# 9. Clean up - delete conversation +client.conversations.delete(conversation_id=conversation.id) +``` + +Multi-modal conversation example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create conversation with image +conversation = client.conversations.create() + +# Add user message with image +client.conversations.items.create( + conversation_id=conversation.id, + items=[ + { + "type": "message", + "role": "user", + "content": [ + { + "type": "text", + "text": "What's in this image?" + }, + { + "type": "image_url", + "image_url": { + "url": "https://example.com/image.jpg" + } + } + ] + } + ] +) + +# Add assistant response +client.conversations.items.create( + conversation_id=conversation.id, + items=[ + { + "type": "message", + "role": "assistant", + "content": [{"type": "text", "text": "I see a beautiful sunset over mountains."}] + } + ] +) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def manage_conversation(): + client = AsyncOpenAI() + + # Create conversation + conversation = await client.conversations.create( + metadata={"user_id": "user_123"} + ) + + # Add message + await client.conversations.items.create( + conversation_id=conversation.id, + items=[ + { + "type": "message", + "role": "user", + "content": [{"type": "text", "text": "Hello!"}] + } + ] + ) + + # List items + async for item in await client.conversations.items.list( + conversation_id=conversation.id + ): + print(item.content) + + # Delete conversation + await client.conversations.delete(conversation_id=conversation.id) + +asyncio.run(manage_conversation()) +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Conversation(BaseModel): + """Conversation object.""" + id: str + created_at: int + object: Literal["conversation"] + metadata: dict[str, str] | None + +class ConversationItem(BaseModel): + """Item within a conversation (union type - can be message, tool call, or tool output).""" + id: str + conversation_id: str + created_at: int + object: Literal["conversation.item"] + # Type-specific fields vary by item type + # See ResponseInputItemParam for complete type definitions + +class ConversationItemList(BaseModel): + """List of conversation items returned from batch create.""" + data: list[ConversationItem] + object: Literal["list"] + +class ConversationDeleted(BaseModel): + """Deletion confirmation.""" + id: str + deleted: bool + object: Literal["conversation.deleted"] + +class SyncConversationCursorPage[T]: + """Cursor-based pagination for conversation items.""" + data: list[T] + has_more: bool + last_id: str | None + def __iter__(self) -> Iterator[T]: ... + +class Omit: + """Sentinel value for omitted parameters.""" +``` + +## Access Pattern + +```python +# Synchronous +from openai import OpenAI +client = OpenAI() +client.conversations.create(...) +client.conversations.retrieve(...) +client.conversations.update(...) +client.conversations.delete(...) +client.conversations.items.create(...) +client.conversations.items.retrieve(...) +client.conversations.items.delete(...) +client.conversations.items.list(...) + +# Asynchronous +from openai import AsyncOpenAI +client = AsyncOpenAI() +await client.conversations.create(...) +await client.conversations.retrieve(...) +await client.conversations.update(...) +await client.conversations.delete(...) +await client.conversations.items.create(...) +await client.conversations.items.retrieve(...) +await client.conversations.items.delete(...) +await client.conversations.items.list(...) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/embeddings.md b/.tessl/tiles/tessl/pypi-openai/docs/embeddings.md new file mode 100644 index 000000000..7d1d03ca3 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/embeddings.md @@ -0,0 +1,269 @@ +# Embeddings + +Create vector embeddings for text inputs to use in semantic search, clustering, recommendations, and other machine learning applications. Embeddings are numerical representations of text that capture semantic meaning. + +## Capabilities + +### Create Embeddings + +Generate vector embeddings for one or more text inputs. + +```python { .api } +def create( + self, + *, + input: str | list[str] | list[int] | list[list[int]], + model: str | EmbeddingModel, + dimensions: int | Omit = omit, + encoding_format: Literal["float", "base64"] | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> CreateEmbeddingResponse: + """ + Create embedding vectors representing the input text. + + Args: + input: Text to embed. Can be: + - Single string: "Hello world" + - List of strings: ["Hello", "world"] + - Token array: [123, 456, 789] + - List of token arrays: [[123, 456], [789, 012]] + Max 8192 tokens per input, 2048 dimensions max for arrays. + Total limit: 300,000 tokens across all inputs per request. + + model: Embedding model ID. Options: + - "text-embedding-3-large": Most capable, 3072 dimensions + - "text-embedding-3-small": Fast and efficient, 1536 dimensions + - "text-embedding-ada-002": Legacy model, 1536 dimensions + + dimensions: Number of dimensions for output embeddings. + Only supported in text-embedding-3 models. Allows reducing + embedding size for storage/performance. Must be ≤ model's max. + + encoding_format: Output format for embeddings. + - "float": List of floats (default) + - "base64": Base64-encoded bytes for space efficiency + + user: Unique end-user identifier for abuse monitoring. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + CreateEmbeddingResponse: Contains embedding vectors and usage info. + + Raises: + BadRequestError: Invalid input or exceeds token limits + AuthenticationError: Invalid API key + RateLimitError: Rate limit exceeded + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Single text embedding +response = client.embeddings.create( + model="text-embedding-3-small", + input="The quick brown fox jumps over the lazy dog" +) + +embedding = response.data[0].embedding +print(f"Embedding dimension: {len(embedding)}") +print(f"First 5 values: {embedding[:5]}") + +# Multiple texts at once +response = client.embeddings.create( + model="text-embedding-3-small", + input=[ + "Machine learning is fascinating", + "I love natural language processing", + "The weather is nice today" + ] +) + +for i, item in enumerate(response.data): + print(f"Embedding {i}: {len(item.embedding)} dimensions") + +# Using larger model with custom dimensions +response = client.embeddings.create( + model="text-embedding-3-large", + input="Semantic search with embeddings", + dimensions=1024 # Reduce from default 3072 +) + +# Base64 encoding for space efficiency +response = client.embeddings.create( + model="text-embedding-3-small", + input="Compressed embedding format", + encoding_format="base64" +) + +# Decode base64 embedding +import base64 +import array + +encoded = response.data[0].embedding +decoded_bytes = base64.b64decode(encoded) +floats = array.array('f', decoded_bytes) +print(f"Decoded embedding: {list(floats)[:5]}") + +# Token-based input (pre-tokenized) +import tiktoken + +enc = tiktoken.encoding_for_model("text-embedding-3-small") +tokens = enc.encode("Hello world") + +response = client.embeddings.create( + model="text-embedding-3-small", + input=tokens +) + +# Semantic search example +def cosine_similarity(a, b): + import math + dot = sum(x * y for x, y in zip(a, b)) + mag_a = math.sqrt(sum(x * x for x in a)) + mag_b = math.sqrt(sum(y * y for y in b)) + return dot / (mag_a * mag_b) + +# Embed documents +documents = [ + "Python is a programming language", + "Machine learning uses algorithms", + "The cat sat on the mat" +] + +response = client.embeddings.create( + model="text-embedding-3-small", + input=documents +) + +doc_embeddings = [item.embedding for item in response.data] + +# Embed query +query = "Tell me about programming" +query_response = client.embeddings.create( + model="text-embedding-3-small", + input=query +) +query_embedding = query_response.data[0].embedding + +# Find most similar document +similarities = [ + cosine_similarity(query_embedding, doc_emb) + for doc_emb in doc_embeddings +] + +best_match_idx = similarities.index(max(similarities)) +print(f"Most similar document: {documents[best_match_idx]}") +print(f"Similarity score: {similarities[best_match_idx]:.4f}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class CreateEmbeddingResponse(BaseModel): + """Response from embeddings endpoint.""" + data: list[Embedding] + model: str + object: Literal["list"] + usage: Usage + +class Embedding(BaseModel): + """Single embedding vector.""" + embedding: list[float] | str # list[float] for "float", str for "base64" + index: int + object: Literal["embedding"] + +class Usage(BaseModel): + """Token usage information.""" + prompt_tokens: int + total_tokens: int + +# Model type +EmbeddingModel = Literal[ + "text-embedding-3-large", + "text-embedding-3-small", + "text-embedding-ada-002" +] +``` + +## Model Comparison + +| Model | Dimensions | Performance | Use Case | +|-------|-----------|-------------|----------| +| text-embedding-3-large | 3072 (default) | Highest quality | Production semantic search, highest accuracy needed | +| text-embedding-3-small | 1536 (default) | Good quality, faster | General purpose, cost-sensitive applications | +| text-embedding-ada-002 | 1536 (fixed) | Legacy performance | Backwards compatibility | + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Batch similar requests for efficiency +texts = ["text1", "text2", "text3"] # Up to 2048 inputs +response = client.embeddings.create( + model="text-embedding-3-small", + input=texts +) + +# 2. Use dimensions parameter to reduce storage +response = client.embeddings.create( + model="text-embedding-3-large", + input="Sample text", + dimensions=256 # Much smaller than default 3072 +) + +# 3. Handle errors gracefully +try: + response = client.embeddings.create( + model="text-embedding-3-small", + input="x" * 10000 # Too long + ) +except Exception as e: + print(f"Error: {e}") + +# 4. Use base64 for space efficiency in storage +response = client.embeddings.create( + model="text-embedding-3-small", + input="Efficient storage", + encoding_format="base64" +) +# Store base64 string directly, decode when needed +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def get_embeddings(): + client = AsyncOpenAI() + + response = await client.embeddings.create( + model="text-embedding-3-small", + input="Async embedding creation" + ) + + return response.data[0].embedding + +# Run async +embeddings = asyncio.run(get_embeddings()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/evals.md b/.tessl/tiles/tessl/pypi-openai/docs/evals.md new file mode 100644 index 000000000..b957f4c4e --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/evals.md @@ -0,0 +1,775 @@ +# Evaluations + +Create and manage evaluations to test model performance with custom testing criteria and data sources. The Evals API enables systematic evaluation of different models and parameters against consistent benchmarks. + +## Capabilities + +### Create Evaluation + +Create an evaluation structure with testing criteria and data source configuration. + +```python { .api } +def create( + self, + *, + data_source_config: dict, + testing_criteria: Iterable[dict], + metadata: dict[str, str] | None | Omit = omit, + name: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Eval: + """ + Create an evaluation for testing model performance. + + Args: + data_source_config: Configuration for the data source used in eval runs. + Dictates the schema of data used in the evaluation. + Example: { + "type": "file", + "file_id": "file-abc123", + "schema": { + "input": {"type": "string"}, + "expected_output": {"type": "string"} + } + } + + testing_criteria: List of graders for all eval runs. Graders can reference + variables in the data source using double curly braces notation like + {{item.variable_name}}. To reference model output, use {{sample.output_text}}. + Example: [ + { + "type": "exact_match", + "expected": "{{item.expected_output}}", + "actual": "{{sample.output_text}}" + }, + { + "type": "contains", + "substring": "{{item.keyword}}", + "text": "{{sample.output_text}}" + } + ] + + metadata: Up to 16 key-value pairs for storing additional information. + Keys max 64 characters, values max 512 characters. + + name: Name of the evaluation for identification. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Eval: Created evaluation object with ID for running evaluations. + + Notes: + - After creating an evaluation, run it on different models/parameters + - See https://platform.openai.com/docs/guides/evals for grader types + - Supported graders: exact_match, contains, llm_judge, custom_code + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create evaluation with testing criteria +eval = client.evals.create( + name="Customer Support Eval", + data_source_config={ + "type": "file", + "file_id": "file-abc123", + "schema": { + "customer_query": {"type": "string"}, + "expected_tone": {"type": "string"}, + "expected_answer": {"type": "string"} + } + }, + testing_criteria=[ + { + "type": "exact_match", + "name": "Answer Correctness", + "expected": "{{item.expected_answer}}", + "actual": "{{sample.output_text}}" + }, + { + "type": "llm_judge", + "name": "Tone Check", + "prompt": "Does the response match the tone: {{item.expected_tone}}?", + "text": "{{sample.output_text}}" + } + ], + metadata={ + "team": "customer-success", + "version": "v1" + } +) + +print(f"Created evaluation: {eval.id}") +``` + +### Retrieve Evaluation + +Get an evaluation by ID. + +```python { .api } +def retrieve( + self, + eval_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Eval: + """ + Retrieve an evaluation by its ID. + + Args: + eval_id: ID of the evaluation to retrieve. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Eval: Evaluation object with full configuration and metadata. + """ +``` + +### Update Evaluation + +Update evaluation properties like name or metadata. + +```python { .api } +def update( + self, + eval_id: str, + *, + metadata: dict[str, str] | None | Omit = omit, + name: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Eval: + """ + Update certain properties of an evaluation. + + Args: + eval_id: ID of the evaluation to update. + + metadata: New metadata key-value pairs. Replaces existing metadata. + Up to 16 pairs, keys max 64 chars, values max 512 chars. + + name: New name for the evaluation. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Eval: Updated evaluation object. + + Notes: + - Only name and metadata can be updated + - Cannot modify data_source_config or testing_criteria after creation + """ +``` + +### List Evaluations + +List all evaluations for the current project. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + order_by: Literal["created_at", "updated_at"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Eval]: + """ + List evaluations for a project. + + Args: + after: Cursor for pagination. ID of last eval from previous request. + + limit: Number of evaluations to retrieve. Default varies by API. + + order: Sort order by timestamp. + - "asc": Ascending (oldest first) + - "desc": Descending (newest first, default) + + order_by: Field to order by. + - "created_at": Creation time (default) + - "updated_at": Last modification time + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[Eval]: Paginated list of evaluations. + Supports iteration: for eval in client.evals.list(): ... + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# List all evaluations +for eval in client.evals.list(): + print(f"{eval.name}: {eval.id}") + +# List with pagination +page = client.evals.list(limit=10) +for eval in page: + print(eval.name) + +# Get next page +if page.has_more: + next_page = client.evals.list( + limit=10, + after=page.data[-1].id + ) + +# List by last updated +for eval in client.evals.list(order_by="updated_at", order="desc"): + print(f"{eval.name} - Updated: {eval.updated_at}") +``` + +### Delete Evaluation + +Delete an evaluation. + +```python { .api } +def delete( + self, + eval_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> EvalDeleteResponse: + """ + Delete an evaluation. + + Args: + eval_id: ID of the evaluation to delete. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + EvalDeleteResponse: Confirmation of deletion with ID and status. + + Notes: + - Deletion is permanent + - Associated eval runs are also deleted + """ +``` + +### Evaluation Runs + +Run an evaluation against a model configuration. + +```python { .api } +# Access via client.evals.runs + +def create( + self, + eval_id: str, + *, + data_source: dict, + metadata: dict[str, str] | None | Omit = omit, + name: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RunCreateResponse: + """ + Create a run of the evaluation with specified data source and configuration. + + Args: + eval_id: ID of the evaluation to run. + + data_source: Details about the run's data source. Can be either: + - File content: {"type": "file_content", "content": [...]} + - File ID: {"type": "file_id", "file_id": "file-xxx"} + The data source will be validated against the schema specified in the evaluation config. + + metadata: Set of 16 key-value pairs that can be attached to an object. + Keys have a maximum length of 64 characters. + Values have a maximum length of 512 characters. + + name: The name of the run. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + RunCreateResponse: Created run object. Use retrieve() to check status and results. + """ + +def retrieve( + self, + eval_id: str, + run_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> EvalRun: + """ + Retrieve a specific evaluation run. + + Args: + eval_id: ID of the evaluation. + run_id: ID of the run. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + EvalRun: Run object with status, results, and scores. + """ + +def cancel( + self, + run_id: str, + *, + eval_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RunCancelResponse: + """ + Cancel an ongoing evaluation run. + + Args: + run_id: ID of the run to cancel. + eval_id: ID of the evaluation. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + RunCancelResponse: Confirmation of run cancellation. + """ + +def delete( + self, + run_id: str, + *, + eval_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RunDeleteResponse: + """ + Permanently delete an evaluation run. + + Args: + run_id: ID of the run to delete. + eval_id: ID of the evaluation. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + RunDeleteResponse: Confirmation of run deletion. + """ + +def list( + self, + eval_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[RunListResponse]: + """ + List all runs for an evaluation. + + Args: + eval_id: ID of the evaluation. + after: Cursor for pagination. + limit: Number of runs to retrieve. + order: Sort order ("asc" or "desc"). + status: Filter by run status ("queued", "in_progress", "completed", "canceled", or "failed"). + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[RunListResponse]: Paginated list of runs. + """ +``` + +### Evaluation Run Output Items + +Inspect individual output items from an evaluation run. + +```python { .api } +# Access via client.evals.runs.output_items + +def retrieve( + self, + output_item_id: str, + *, + eval_id: str, + run_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> OutputItemRetrieveResponse: + """ + Get an evaluation run output item by ID. + + Args: + output_item_id: ID of the output item to retrieve. + + eval_id: ID of the evaluation. + + run_id: ID of the evaluation run. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + OutputItemRetrieveResponse: Individual output item with test results + and grader scores for a specific data point in the evaluation. + """ + +def list( + self, + run_id: str, + *, + eval_id: str, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + status: Literal["fail", "pass"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[OutputItemListResponse]: + """ + Get a list of output items for an evaluation run. + + Args: + run_id: ID of the evaluation run. + + eval_id: ID of the evaluation. + + after: Cursor for pagination. ID of last output item from previous request. + + limit: Number of output items to retrieve. + + order: Sort order by timestamp. + - "asc": Ascending (oldest first) + - "desc": Descending (newest first, default) + + status: Filter output items by status. + - "fail": Only failed output items + - "pass": Only passed output items + Omit to retrieve all output items. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[OutputItemListResponse]: Paginated list of output items. + Each output item contains the model output and grader results for + a single test case in the evaluation run. + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# List all output items for a run +for output_item in client.evals.runs.output_items.list( + eval_id="eval-abc123", + run_id="run-def456" +): + print(f"Item {output_item.id}: {output_item.status}") + if output_item.status == "fail": + print(f" Failed on: {output_item.grader_results}") + +# Filter only failed items +failed_items = client.evals.runs.output_items.list( + eval_id="eval-abc123", + run_id="run-def456", + status="fail" +) + +for item in failed_items: + print(f"Failed item: {item.id}") + # Retrieve detailed information + detail = client.evals.runs.output_items.retrieve( + eval_id="eval-abc123", + run_id="run-def456", + output_item_id=item.id + ) + print(f" Model output: {detail.model_output}") + print(f" Expected: {detail.expected}") + +# Paginate through all output items +page = client.evals.runs.output_items.list( + eval_id="eval-abc123", + run_id="run-def456", + limit=10, + order="asc" +) + +for item in page: + print(f"{item.id}: {item.status}") +``` + +Complete workflow example: + +```python +from openai import OpenAI +import time + +client = OpenAI() + +# 1. Create evaluation +eval = client.evals.create( + name="Model Comparison", + data_source_config={ + "type": "file", + "file_id": "file-abc123" + }, + testing_criteria=[ + { + "type": "exact_match", + "expected": "{{item.expected}}", + "actual": "{{sample.output_text}}" + } + ] +) + +# 2. Run evaluation with different models +run_gpt4 = client.evals.runs.create( + eval_id=eval.id, + model="gpt-4", + temperature=0.7, + metadata={"variant": "gpt-4"} +) + +run_gpt35 = client.evals.runs.create( + eval_id=eval.id, + model="gpt-3.5-turbo", + temperature=0.7, + metadata={"variant": "gpt-3.5"} +) + +# 3. Wait for completion and check results +def wait_for_run(eval_id: str, run_id: str): + while True: + run = client.evals.runs.retrieve(eval_id=eval_id, run_id=run_id) + if run.status in ["completed", "failed", "cancelled"]: + return run + time.sleep(2) + +gpt4_results = wait_for_run(eval.id, run_gpt4.id) +gpt35_results = wait_for_run(eval.id, run_gpt35.id) + +print(f"GPT-4 Score: {gpt4_results.score}") +print(f"GPT-3.5 Score: {gpt35_results.score}") + +# 4. Compare results +for run in client.evals.runs.list(eval_id=eval.id): + print(f"{run.model}: {run.score} ({run.status})") + +# 5. Update evaluation name +client.evals.update( + eval_id=eval.id, + name="Model Comparison - Updated", + metadata={"status": "active"} +) + +# 6. Clean up +client.evals.delete(eval_id=eval.id) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def run_eval(): + client = AsyncOpenAI() + + # Create evaluation + eval = await client.evals.create( + name="Async Eval", + data_source_config={"type": "file", "file_id": "file-abc"}, + testing_criteria=[{"type": "exact_match", "expected": "{{item.expected}}", "actual": "{{sample.output_text}}"}] + ) + + # Run evaluation + run = await client.evals.runs.create( + eval_id=eval.id, + model="gpt-4" + ) + + # Check results + result = await client.evals.runs.retrieve( + eval_id=eval.id, + run_id=run.id + ) + + return result.score + +score = asyncio.run(run_eval()) +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Eval(BaseModel): + """Evaluation object.""" + id: str + created_at: int + updated_at: int + name: str | None + object: Literal["eval"] + data_source_config: dict + testing_criteria: list[dict] + metadata: dict[str, str] | None + +class EvalRun(BaseModel): + """Evaluation run object.""" + id: str + eval_id: str + created_at: int + model: str + status: Literal["queued", "in_progress", "completed", "failed", "cancelled"] + score: float | None # Present when status="completed" + results: list[dict] | None # Detailed results per test case + metadata: dict[str, str] | None + +class EvalDeleteResponse(BaseModel): + """Response from delete operation.""" + id: str + deleted: bool + object: Literal["eval.deleted"] + +class OutputItemRetrieveResponse(BaseModel): + """Individual evaluation run output item.""" + id: str + eval_id: str + run_id: str + status: Literal["fail", "pass"] + model_output: str | dict # Model's generated output + expected: str | dict | None # Expected output if defined + grader_results: list[dict] # Results from each grader + created_at: int + +class OutputItemListResponse(BaseModel): + """Evaluation run output item in list responses.""" + id: str + eval_id: str + run_id: str + status: Literal["fail", "pass"] + created_at: int + +class SyncCursorPage[T]: + """Cursor-based pagination.""" + data: list[T] + has_more: bool + def __iter__(self) -> Iterator[T]: ... + +class Omit: + """Sentinel value for omitted parameters.""" +``` + +## Access Pattern + +```python +# Synchronous +from openai import OpenAI +client = OpenAI() +client.evals.create(...) +client.evals.retrieve(...) +client.evals.update(...) +client.evals.list(...) +client.evals.delete(...) +client.evals.runs.create(...) +client.evals.runs.retrieve(...) +client.evals.runs.list(...) +client.evals.runs.output_items.retrieve(...) +client.evals.runs.output_items.list(...) + +# Asynchronous +from openai import AsyncOpenAI +client = AsyncOpenAI() +await client.evals.create(...) +await client.evals.retrieve(...) +await client.evals.update(...) +await client.evals.list(...) +await client.evals.delete(...) +await client.evals.runs.create(...) +await client.evals.runs.retrieve(...) +await client.evals.runs.list(...) +await client.evals.runs.output_items.retrieve(...) +await client.evals.runs.output_items.list(...) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/files.md b/.tessl/tiles/tessl/pypi-openai/docs/files.md new file mode 100644 index 000000000..aa2298c8e --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/files.md @@ -0,0 +1,512 @@ +# Files + +Upload and manage files for use with OpenAI features like Assistants, Fine-tuning, Batch processing, and Vision. Provides file storage with purpose-specific handling. + +## Capabilities + +### Upload File + +Upload a file for use with OpenAI services. + +```python { .api } +def create( + self, + *, + file: FileTypes, + purpose: FilePurpose, + expires_after: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FileObject: + """ + Upload a file for use with OpenAI services. + + Args: + file: File to upload. Can be file path string, file object, or tuple. + Maximum file size varies by purpose. + + purpose: Intended purpose of the file. Options: + - "assistants": For Assistants API and file_search tool + - "batch": For Batch API operations + - "fine-tune": For fine-tuning jobs + - "vision": For vision model inputs + - "user_data": Flexible file type for any purpose + - "evals": For evaluation data sets + + expires_after: Expiration policy for the file (ExpiresAfter type). By default, + files with purpose="batch" expire after 30 days and all other files persist + until manually deleted. Structure: + - anchor: "created_at" (file creation time) + - seconds: int (3600-2592000, time in seconds until expiration) + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FileObject: Uploaded file metadata including ID. + + Raises: + BadRequestError: Invalid file format, size, or purpose + AuthenticationError: Invalid API key + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Upload file for assistants +with open("document.pdf", "rb") as file: + response = client.files.create( + file=file, + purpose="assistants" + ) + +file_id = response.id +print(f"File ID: {file_id}") + +# Upload for fine-tuning +with open("training_data.jsonl", "rb") as file: + response = client.files.create( + file=file, + purpose="fine-tune" + ) + +# Upload for batch processing +with open("batch_requests.jsonl", "rb") as file: + response = client.files.create( + file=file, + purpose="batch" + ) + +# Using file_from_path helper +from openai import file_from_path + +response = client.files.create( + file=file_from_path("data.csv"), + purpose="assistants" +) + +# Check upload details +print(f"Filename: {response.filename}") +print(f"Size: {response.bytes} bytes") +print(f"Purpose: {response.purpose}") +print(f"Status: {response.status}") +``` + +### Retrieve File Metadata + +Get information about a specific file. + +```python { .api } +def retrieve( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FileObject: + """ + Retrieve file metadata. + + Args: + file_id: The ID of the file to retrieve. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FileObject: File metadata. + + Raises: + NotFoundError: File not found + """ +``` + +Usage example: + +```python +file = client.files.retrieve("file-abc123") + +print(f"Filename: {file.filename}") +print(f"Purpose: {file.purpose}") +print(f"Size: {file.bytes} bytes") +print(f"Created: {file.created_at}") +``` + +### List Files + +List all uploaded files with optional filtering. + +```python { .api } +def list( + self, + *, + purpose: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + after: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[FileObject]: + """ + List uploaded files with optional filtering and pagination. + + Args: + purpose: Filter by file purpose (e.g., "assistants", "fine-tune"). + limit: Number of files to retrieve (max 10000). Default 10000. + order: Sort order. "asc" for ascending, "desc" for descending. Default "desc". + after: Cursor for pagination. Return files after this file ID. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[FileObject]: Cursor-paginated list of files. + """ +``` + +Usage examples: + +```python +# List all files +files = client.files.list() + +for file in files.data: + print(f"{file.filename} ({file.id})") + +# Filter by purpose +assistant_files = client.files.list(purpose="assistants") + +# Pagination +page1 = client.files.list(limit=10) +page2 = client.files.list(limit=10, after=page1.data[-1].id) + +# Iterate through all files +for file in client.files.list(): + print(file.filename) +``` + +### Delete File + +Delete a file from OpenAI storage. + +```python { .api } +def delete( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FileDeleted: + """ + Delete a file. + + Args: + file_id: The ID of the file to delete. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FileDeleted: Deletion confirmation. + + Raises: + NotFoundError: File not found + """ +``` + +Usage example: + +```python +# Delete file +result = client.files.delete("file-abc123") + +print(f"Deleted: {result.id}") +print(f"Success: {result.deleted}") +``` + +### Download File Content + +Retrieve the binary content of a file. + +```python { .api } +def content( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> HttpxBinaryResponseContent: + """ + Retrieve file content. + + Args: + file_id: The ID of the file to download. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + HttpxBinaryResponseContent: File content as binary data. + + Raises: + NotFoundError: File not found + """ +``` + +Usage example: + +```python +from pathlib import Path + +# Download file content +content = client.files.content("file-abc123") + +# Save to file +Path("downloaded_file.txt").write_bytes(content.content) + +# Or use read() +file_bytes = content.read() + +# Stream to file +content.stream_to_file("output.txt") +``` + +### Retrieve File Content (Alias) + +Retrieve the textual content of a file. This is an alias method that retrieves content and returns it as a string. + +```python { .api } +def retrieve_content( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> str: + """ + Retrieve file content as a string. + + This method retrieves the contents of the specified file and returns + it as a string. For binary content, use the content() method instead. + + Args: + file_id: The ID of the file to retrieve content from. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + str: The file content as a string. + + Raises: + NotFoundError: File not found + """ +``` + +Usage example: + +```python +# Retrieve file content as string +text_content = client.files.retrieve_content("file-abc123") +print(text_content) +``` + +### Wait for Processing + +Poll until file processing is complete (helper method). + +```python { .api } +def wait_for_processing( + self, + file_id: str, + *, + poll_interval: float = 5.0, + max_wait_seconds: float = 1800, +) -> FileObject: + """ + Wait for file processing to complete. + + Args: + file_id: The ID of the file to wait for. + poll_interval: Seconds between status checks. Default 5.0. + max_wait_seconds: Maximum seconds to wait. Default 1800 (30 minutes). + + Returns: + FileObject: File with completed status. + + Raises: + TimeoutError: Processing not completed within max_wait_seconds + APIError: Processing failed + """ +``` + +Usage example: + +```python +# Upload and wait +with open("large_file.pdf", "rb") as file: + uploaded = client.files.create(file=file, purpose="assistants") + +# Wait for processing +ready_file = client.files.wait_for_processing(uploaded.id) + +print(f"File ready: {ready_file.status}") +``` + +## Types + +```python { .api } +from typing import Literal, TypedDict, Required, Union, Iterator +from pydantic import BaseModel + +class FileObject(BaseModel): + """File metadata.""" + id: str + bytes: int + created_at: int + filename: str + object: Literal["file"] + purpose: FilePurpose + status: FileStatus + status_details: str | None + +class FileDeleted(BaseModel): + """File deletion confirmation.""" + id: str + deleted: bool + object: Literal["file"] + +FilePurpose = Literal[ + "assistants", + "assistants_output", + "batch", + "batch_output", + "fine-tune", + "fine-tune-results", + "vision", + "user_data", + "evals" +] + +FileStatus = Literal["uploaded", "processed", "error"] + +class ExpiresAfter(TypedDict): + """File expiration policy configuration.""" + anchor: Required[Literal["created_at"]] + """Anchor timestamp after which the expiration policy applies. Currently only 'created_at' is supported.""" + + seconds: Required[int] + """Number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).""" + +# File types +FileTypes = Union[ + FileContent, # File-like object + tuple[str | None, FileContent], # (filename, content) + tuple[str | None, FileContent, str | None] # (filename, content, content_type) +] + +# Pagination +class SyncPage[T](BaseModel): + data: list[T] + object: str + has_more: bool + def __iter__(self) -> Iterator[T]: ... +``` + +## File Size Limits + +| Purpose | Format | Max Size | +|---------|--------|----------| +| assistants | Various | 512 MB | +| batch | JSONL | 100 MB | +| fine-tune | JSONL | 1 GB | +| vision | Images | 20 MB | + +## Best Practices + +```python +from openai import OpenAI +from pathlib import Path + +client = OpenAI() + +# 1. Check file exists before upload +file_path = Path("data.txt") +if file_path.exists(): + with open(file_path, "rb") as f: + file = client.files.create(file=f, purpose="assistants") + +# 2. Clean up unused files +files = client.files.list(purpose="assistants") +for file in files: + if should_delete(file): + client.files.delete(file.id) + +# 3. Handle upload errors +from openai import APIError + +try: + with open("large_file.pdf", "rb") as f: + file = client.files.create(file=f, purpose="assistants") +except APIError as e: + print(f"Upload failed: {e}") + +# 4. Track file IDs for later use +uploaded_files = [] + +for file_path in ["file1.txt", "file2.txt"]: + with open(file_path, "rb") as f: + file = client.files.create(file=f, purpose="assistants") + uploaded_files.append(file.id) + +# Use files with assistant +assistant = client.beta.assistants.create( + model="gpt-4", + tools=[{"type": "file_search"}], + tool_resources={"file_search": {"file_ids": uploaded_files}} +) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def upload_file(): + client = AsyncOpenAI() + + with open("document.pdf", "rb") as file: + response = await client.files.create( + file=file, + purpose="assistants" + ) + + return response.id + +file_id = asyncio.run(upload_file()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md b/.tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md new file mode 100644 index 000000000..776da1e0f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md @@ -0,0 +1,845 @@ +# Fine-tuning + +Create and manage fine-tuning jobs to customize models on your own data. Fine-tune base models to improve performance on specific tasks, add domain knowledge, or adjust model behavior. + +## Capabilities + +### Create Fine-tuning Job + +Start a fine-tuning job to train a custom model. + +```python { .api } +def create( + self, + *, + model: str, + training_file: str, + hyperparameters: dict | Omit = omit, + method: dict | Omit = omit, + integrations: list[dict] | Omit = omit, + metadata: dict[str, str] | Omit = omit, + seed: int | Omit = omit, + suffix: str | Omit = omit, + validation_file: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FineTuningJob: + """ + Create a fine-tuning job to train a custom model. + + Args: + model: Base model to fine-tune. Supported models: + - "gpt-4o-2024-08-06" + - "gpt-4o-mini-2024-07-18" + - "gpt-3.5-turbo" + - "babbage-002", "davinci-002" + + training_file: ID of uploaded training file (JSONL format). + Each line: {"messages": [...], "response": ...} + + hyperparameters: Training hyperparameters. Options: + - n_epochs: Number of training epochs (auto or 1-50) + - batch_size: Batch size (auto or specific value) + - learning_rate_multiplier: Learning rate multiplier (auto or 0.02-2) + + method: Fine-tuning method configuration. Options: + - {"type": "supervised"}: Standard supervised fine-tuning + - {"type": "dpo"}: Direct Preference Optimization + - {"type": "reinforcement"}: Reinforcement learning + + integrations: External tool integrations. Options: + - Weights & Biases: [{"type": "wandb", "wandb": {"project": "..."}}] + + metadata: Key-value pairs for storing additional information (max 16 pairs). + Keys max 64 chars, values max 512 chars. + + seed: Random seed for reproducibility. + + suffix: Custom suffix for fine-tuned model name (max 18 chars). + Final model name: ft:{base_model}:{suffix}:{job_id} + + validation_file: ID of validation file for evaluation during training. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FineTuningJob: Created job with status "created" or "validating_files". + + Raises: + BadRequestError: Invalid parameters or file format + NotFoundError: Training file not found + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Upload training data first +with open("training_data.jsonl", "rb") as f: + training_file = client.files.create(file=f, purpose="fine-tune") + +# Create fine-tuning job +job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file=training_file.id +) + +print(f"Job ID: {job.id}") +print(f"Status: {job.status}") + +# With hyperparameters +job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file=training_file.id, + hyperparameters={ + "n_epochs": 3, + "batch_size": 4, + "learning_rate_multiplier": 0.1 + } +) + +# With validation file +with open("validation_data.jsonl", "rb") as f: + validation_file = client.files.create(file=f, purpose="fine-tune") + +job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file=training_file.id, + validation_file=validation_file.id, + suffix="my-custom-model" +) + +# With Weights & Biases integration +job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file=training_file.id, + integrations=[{ + "type": "wandb", + "wandb": { + "project": "my-fine-tuning", + "name": "experiment-1" + } + }] +) + +# Using DPO method +job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file=training_file.id, + method={"type": "dpo"} +) +``` + +### Retrieve Fine-tuning Job + +Get status and details of a fine-tuning job. + +```python { .api } +def retrieve( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FineTuningJob: + """ + Retrieve fine-tuning job details. + + Args: + fine_tuning_job_id: The ID of the fine-tuning job. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FineTuningJob: Job details including current status. + + Raises: + NotFoundError: Job not found + """ +``` + +Usage example: + +```python +# Check job status +job = client.fine_tuning.jobs.retrieve("ftjob-abc123") + +print(f"Status: {job.status}") +print(f"Model: {job.fine_tuned_model}") +print(f"Trained tokens: {job.trained_tokens}") + +if job.status == "succeeded": + print(f"Fine-tuned model ready: {job.fine_tuned_model}") +elif job.status == "failed": + print(f"Error: {job.error}") +``` + +### List Fine-tuning Jobs + +List fine-tuning jobs with optional filtering and pagination. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + metadata: dict[str, str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[FineTuningJob]: + """ + List fine-tuning jobs with pagination. + + Args: + after: Cursor for pagination. Return jobs after this job ID. + limit: Number of jobs to retrieve (max 100). Default 20. + metadata: Optional metadata filter. To filter, use the syntax `metadata[k]=v`. + Alternatively, set `metadata=null` to indicate no metadata. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[FineTuningJob]: Paginated list of jobs. + """ +``` + +Usage example: + +```python +# List all jobs +jobs = client.fine_tuning.jobs.list() + +for job in jobs: + print(f"{job.id}: {job.status}") + +# Pagination +page1 = client.fine_tuning.jobs.list(limit=10) +page2 = client.fine_tuning.jobs.list(limit=10, after=page1.data[-1].id) + +# Filter by status +succeeded_jobs = [ + job for job in client.fine_tuning.jobs.list() + if job.status == "succeeded" +] +``` + +### Cancel Fine-tuning Job + +Cancel an in-progress fine-tuning job. + +```python { .api } +def cancel( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FineTuningJob: + """ + Cancel a running fine-tuning job. + + Args: + fine_tuning_job_id: The ID of the job to cancel. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FineTuningJob: Job with status "cancelled". + + Raises: + NotFoundError: Job not found + BadRequestError: Job not in cancellable state + """ +``` + +Usage example: + +```python +# Cancel job +job = client.fine_tuning.jobs.cancel("ftjob-abc123") + +print(f"Status: {job.status}") # "cancelled" +``` + +### Pause Fine-tuning Job + +Temporarily pause a running fine-tuning job to save costs. + +```python { .api } +def pause( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FineTuningJob: + """ + Pause a running fine-tuning job. + + Args: + fine_tuning_job_id: The ID of the job to pause. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FineTuningJob: Job with status "paused". + + Raises: + NotFoundError: Job not found + BadRequestError: Job not in pauseable state + """ +``` + +### Resume Fine-tuning Job + +Resume a previously paused fine-tuning job. + +```python { .api } +def resume( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> FineTuningJob: + """ + Resume a paused fine-tuning job. + + Args: + fine_tuning_job_id: The ID of the job to resume. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + FineTuningJob: Job with status "running". + + Raises: + NotFoundError: Job not found + BadRequestError: Job not in resumeable state + """ +``` + +Usage example: + +```python +# Pause job to save costs +job = client.fine_tuning.jobs.pause("ftjob-abc123") +print(f"Status: {job.status}") # "paused" + +# Resume later +job = client.fine_tuning.jobs.resume("ftjob-abc123") +print(f"Status: {job.status}") # "running" +``` + +### List Job Events + +Stream events and training metrics from a fine-tuning job. + +```python { .api } +def list_events( + self, + fine_tuning_job_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[FineTuningJobEvent]: + """ + List events for a fine-tuning job (training logs, metrics). + + Args: + fine_tuning_job_id: The job ID. + after: Return events after this event ID. + limit: Number of events to retrieve (max 100). Default 20. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[FineTuningJobEvent]: Job events and logs. + """ +``` + +Usage example: + +```python +# Get training events +events = client.fine_tuning.jobs.list_events("ftjob-abc123") + +for event in events: + print(f"[{event.created_at}] {event.message}") + if event.data: + print(f" Metrics: {event.data}") + +# Monitor training progress +import time + +job_id = "ftjob-abc123" +while True: + job = client.fine_tuning.jobs.retrieve(job_id) + + if job.status in ["succeeded", "failed", "cancelled"]: + break + + events = client.fine_tuning.jobs.list_events(job_id, limit=5) + for event in events: + print(event.message) + + time.sleep(10) +``` + +### List Checkpoints + +List checkpoints saved during fine-tuning. + +```python { .api } +def list( + self, + fine_tuning_job_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[FineTuningJobCheckpoint]: + """ + List checkpoints for a fine-tuning job. + + Args: + fine_tuning_job_id: The job ID. + after: Return checkpoints after this checkpoint ID. + limit: Number of checkpoints to retrieve. Default 10. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[FineTuningJobCheckpoint]: Job checkpoints. + """ +``` + +Usage example: + +```python +# List checkpoints +checkpoints = client.fine_tuning.jobs.checkpoints.list("ftjob-abc123") + +for checkpoint in checkpoints: + print(f"Step {checkpoint.step_number}: {checkpoint.metrics}") + print(f" Model: {checkpoint.fine_tuned_model_checkpoint}") +``` + +### Checkpoint Permissions + +Manage access permissions for fine-tuning checkpoints to enable sharing. + +```python { .api } +# Accessed via: client.fine_tuning.checkpoints.permissions +def create( + self, + fine_tuned_model_checkpoint: str, + *, + project_ids: list[str], + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncPage[PermissionCreateResponse]: + """ + Grant access permission to a checkpoint. + + NOTE: Calling this endpoint requires an admin API key. + This enables organization owners to share fine-tuned models with other projects + in their organization. + + Args: + fine_tuned_model_checkpoint: The checkpoint model ID (e.g., "ftckpt-xyz789"). + project_ids: The project identifiers to grant access to (list of strings). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncPage[PermissionCreateResponse]: Paginated list of created permissions. + """ + +def retrieve( + self, + fine_tuned_model_checkpoint: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["ascending", "descending"] | Omit = omit, + project_id: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> PermissionRetrieveResponse: + """ + List all permissions for a checkpoint. + + NOTE: This endpoint requires an admin API key. + Organization owners can use this endpoint to view all permissions for a + fine-tuned model checkpoint. + + Args: + fine_tuned_model_checkpoint: The checkpoint model ID. + after: Identifier for the last permission ID from the previous pagination request. + limit: Number of permissions to retrieve. + order: The order in which to retrieve permissions ("ascending" or "descending"). + project_id: The ID of the project to get permissions for (filter). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + PermissionRetrieveResponse: Permissions list response. + """ + +def delete( + self, + permission_id: str, + *, + fine_tuned_model_checkpoint: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> PermissionDeleteResponse: + """ + Revoke a checkpoint permission. + + NOTE: This endpoint requires an admin API key. + Organization owners can use this endpoint to delete a permission for a + fine-tuned model checkpoint. + + Args: + permission_id: The ID of the permission to delete (positional parameter). + fine_tuned_model_checkpoint: The checkpoint model ID (keyword-only parameter). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + PermissionDeleteResponse: Deletion confirmation. + """ +``` + +Usage example: + +```python +# Grant checkpoint access to other projects +permissions = client.fine_tuning.checkpoints.permissions.create( + fine_tuned_model_checkpoint="ftckpt-xyz789", + project_ids=["proj-456", "proj-789"] +) + +print(f"Created {len(permissions.data)} permissions") +for perm in permissions: + print(f" Permission ID: {perm.id}") + +# Retrieve all permissions for a checkpoint +all_perms = client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ftckpt-xyz789" +) + +print(f"Total permissions: {len(all_perms.data)}") +for perm in all_perms.data: + print(f" {perm.id}: Project {perm.project_id}") + +# Filter permissions by project +project_perms = client.fine_tuning.checkpoints.permissions.retrieve( + fine_tuned_model_checkpoint="ftckpt-xyz789", + project_id="proj-456" +) + +# Revoke a permission +deleted = client.fine_tuning.checkpoints.permissions.delete( + permission_id="perm-abc123", + fine_tuned_model_checkpoint="ftckpt-xyz789" +) + +print(f"Deleted: {deleted.deleted}") # True +``` + +### Alpha Graders + +Run graders to evaluate fine-tuning results (alpha feature). + +```python { .api } +# Accessed via: client.fine_tuning.alpha.graders +def run( + self, + *, + grader: dict, + model_sample: str, + item: object | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> GraderRunResponse: + """ + Run a grader on a model sample. + + Args: + grader: The grader used for the fine-tuning job (structured Grader object). + model_sample: The model sample to be evaluated. This value will be used to + populate the `sample` namespace. See the guide for more details. + The `output_json` variable will be populated if the model sample is + a valid JSON string. + item: The dataset item provided to the grader. This will be used to + populate the `item` namespace. See the guide for more details. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + GraderRunResponse: Grading results with scores and analysis. + """ + +def validate( + self, + *, + grader: dict, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> GraderValidateResponse: + """ + Validate a grader configuration before running. + + Args: + grader: Grader configuration to validate (structured Grader object). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ValidationResult: Validation status and any errors. + """ +``` + +Usage example: + +```python +# Validate grader configuration +validation = client.fine_tuning.alpha.graders.validate( + grader_config={ + "type": "model_grader", + "criteria": "accuracy", + "model": "gpt-4" + } +) + +if validation.valid: + # Run grader on evaluation data + result = client.fine_tuning.alpha.graders.run( + grader_config={ + "type": "model_grader", + "criteria": "accuracy" + }, + evaluation_data=[ + { + "input": "What is 2+2?", + "expected": "4", + "actual": "4" + } + ], + model="ft:gpt-3.5-turbo:my-org:custom_suffix:id" + ) + + print(f"Score: {result.score}") + print(f"Analysis: {result.analysis}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class FineTuningJob(BaseModel): + """Fine-tuning job.""" + id: str + created_at: int + error: dict | None + fine_tuned_model: str | None + finished_at: int | None + hyperparameters: dict + model: str + object: Literal["fine_tuning.job"] + organization_id: str + result_files: list[str] + seed: int + status: Literal[ + "validating_files", "queued", "running", + "succeeded", "failed", "cancelled" + ] + trained_tokens: int | None + training_file: str + validation_file: str | None + integrations: list[dict] | None + user_provided_suffix: str | None + +class FineTuningJobEvent(BaseModel): + """Job event/log entry.""" + id: str + created_at: int + level: Literal["info", "warn", "error"] + message: str + object: Literal["fine_tuning.job.event"] + data: dict | None + type: str + +class FineTuningJobCheckpoint(BaseModel): + """Training checkpoint.""" + id: str + created_at: int + fine_tuned_model_checkpoint: str + fine_tuning_job_id: str + metrics: dict + object: Literal["fine_tuning.job.checkpoint"] + step_number: int + +# Pagination +class SyncCursorPage[T](BaseModel): + data: list[T] + object: str + has_more: bool + def __iter__(self) -> Iterator[T]: ... +``` + +## Training Data Format + +Training data must be JSONL (JSON Lines) format: + +```jsonl +{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 2+2?"}, {"role": "assistant", "content": "4"}]} +{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the capital of France?"}, {"role": "assistant", "content": "Paris"}]} +``` + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Validate training data before upload +def validate_jsonl(file_path: str) -> bool: + import json + + with open(file_path) as f: + for i, line in enumerate(f): + try: + data = json.loads(line) + assert "messages" in data + except Exception as e: + print(f"Line {i}: {e}") + return False + return True + +# 2. Monitor job progress +def wait_for_job(job_id: str): + import time + + while True: + job = client.fine_tuning.jobs.retrieve(job_id) + + if job.status == "succeeded": + return job.fine_tuned_model + elif job.status in ["failed", "cancelled"]: + raise Exception(f"Job {job.status}: {job.error}") + + print(f"Status: {job.status}") + time.sleep(30) + +# 3. Use validation file for monitoring +with open("train.jsonl", "rb") as train_f, \ + open("val.jsonl", "rb") as val_f: + + train_file = client.files.create(file=train_f, purpose="fine-tune") + val_file = client.files.create(file=val_f, purpose="fine-tune") + + job = client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file=train_file.id, + validation_file=val_file.id + ) + +# 4. Clean up after completion +fine_tuned_model = wait_for_job(job.id) + +# Use the model +response = client.chat.completions.create( + model=fine_tuned_model, + messages=[{"role": "user", "content": "Test"}] +) + +# Later, delete if no longer needed +client.models.delete(fine_tuned_model) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_fine_tune(): + client = AsyncOpenAI() + + job = await client.fine_tuning.jobs.create( + model="gpt-3.5-turbo", + training_file="file-abc123" + ) + + return job.id + +job_id = asyncio.run(create_fine_tune()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/images.md b/.tessl/tiles/tessl/pypi-openai/docs/images.md new file mode 100644 index 000000000..aee44b411 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/images.md @@ -0,0 +1,617 @@ +# Images + +Generate, edit, and create variations of images using DALL-E models. Supports creating images from text prompts, editing existing images with masks, and generating variations. + +## Capabilities + +### Generate Images + +Create images from text descriptions using DALL-E or GPT-Image-1 models. + +```python { .api } +def generate( + self, + *, + prompt: str, + background: Literal["transparent", "opaque", "auto"] | Omit = omit, + model: str | ImageModel | Omit = omit, + moderation: Literal["low", "auto"] | Omit = omit, + n: int | Omit = omit, + output_compression: int | Omit = omit, + output_format: Literal["png", "jpeg", "webp"] | Omit = omit, + partial_images: int | Omit = omit, + quality: Literal["standard", "hd", "low", "medium", "high", "auto"] | Omit = omit, + response_format: Literal["url", "b64_json"] | Omit = omit, + size: Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] | Omit = omit, + stream: bool | Omit = omit, + style: Literal["vivid", "natural"] | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ImagesResponse | Stream[ImageGenStreamEvent]: + """ + Generate images from text prompts. + + Args: + prompt: Text description of the desired image(s). Max length: + - gpt-image-1: 32000 characters + - dall-e-3: 4000 characters + - dall-e-2: 1000 characters + + background: (gpt-image-1 only) Background transparency control. Options: + - "transparent": Transparent background (requires png or webp output) + - "opaque": Opaque background + - "auto": Model determines best option (default) + + model: Model to use. Options: + - "dall-e-3": Latest DALL-E, highest quality + - "dall-e-2": Previous DALL-E generation + - "gpt-image-1": Latest generation model with advanced features + Defaults to dall-e-2 unless gpt-image-1 specific parameters are used. + + moderation: (gpt-image-1 only) Content moderation level. Options: + - "auto": Standard filtering (default) + - "low": Less restrictive filtering + + n: Number of images to generate. Default 1. + - gpt-image-1: Supports 1-10 + - dall-e-3: Only supports n=1 + - dall-e-2: Supports 1-10 + + output_compression: (gpt-image-1 only) Compression level (0-100%) for webp or jpeg + output formats. Default 100. Higher values = better quality, larger file size. + + output_format: (gpt-image-1 only) Output image format. Options: + - "png": PNG format (default), supports transparency + - "jpeg": JPEG format, smaller files, no transparency + - "webp": WebP format, good compression with quality + + partial_images: (gpt-image-1 only) Number of partial images for streaming (0-3). + Set to 0 for single final image. Default returns progressive partial images. + Final image may arrive before all partials if generation completes quickly. + + quality: Image quality. Options depend on model: + - gpt-image-1: "auto" (default), "high", "medium", "low" + - dall-e-3: "standard" (default), "hd" + - dall-e-2: Only "standard" + + response_format: Response data format. Options: + - "url": Returns URL (default), expires after 1 hour (dall-e-2 and dall-e-3 only) + - "b64_json": Returns base64-encoded JSON + Note: gpt-image-1 always returns base64-encoded images. + + size: Image dimensions. Options depend on model: + - gpt-image-1: "auto" (default), "1024x1024", "1536x1024", "1024x1536" + - dall-e-3: "1024x1024", "1792x1024", "1024x1792" + - dall-e-2: "256x256", "512x512", "1024x1024" + + stream: (gpt-image-1 only) If true, returns Stream[ImageGenStreamEvent] for + progressive image generation. Set partial_images to control streaming behavior. + + style: Visual style. Only for dall-e-3. Options: + - "vivid": Hyper-real and dramatic (default) + - "natural": More natural, less hyper-real + + user: Unique end-user identifier for abuse monitoring. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ImagesResponse: Contains generated image(s) when stream=False. + Stream[ImageGenStreamEvent]: Streaming events when stream=True (gpt-image-1 only). + + Raises: + BadRequestError: Invalid parameters or prompt violates content policy + RateLimitError: Rate limit exceeded + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic image generation +response = client.images.generate( + model="dall-e-3", + prompt="A cute baby sea otter wearing a beret", + n=1, + size="1024x1024" +) + +image_url = response.data[0].url +print(f"Image URL: {image_url}") + +# HD quality +response = client.images.generate( + model="dall-e-3", + prompt="A serene mountain landscape at sunset", + quality="hd", + size="1792x1024" +) + +# Natural style +response = client.images.generate( + model="dall-e-3", + prompt="A portrait of a programmer at work", + style="natural" +) + +# Base64 response (no expiration) +response = client.images.generate( + model="dall-e-3", + prompt="A futuristic cityscape", + response_format="b64_json" +) + +import base64 +from pathlib import Path + +# Save base64 image +image_data = base64.b64decode(response.data[0].b64_json) +Path("generated_image.png").write_bytes(image_data) + +# Download from URL +import requests + +response = client.images.generate( + model="dall-e-3", + prompt="A cat playing chess" +) + +image_url = response.data[0].url +image_response = requests.get(image_url) +Path("cat_chess.png").write_bytes(image_response.content) + +# Multiple images with DALL-E 2 +response = client.images.generate( + model="dall-e-2", + prompt="Abstract art with geometric shapes", + n=4, + size="512x512" +) + +for i, image in enumerate(response.data): + print(f"Image {i+1}: {image.url}") + +# Check revised prompt (DALL-E 3 may modify prompts) +response = client.images.generate( + model="dall-e-3", + prompt="A dog" +) + +print(f"Original prompt: A dog") +print(f"Revised prompt: {response.data[0].revised_prompt}") +``` + +### Edit Images + +Modify an existing image using a mask and text prompt. Supports both DALL-E 2 and GPT-Image-1 models. + +```python { .api } +def edit( + self, + *, + image: FileTypes | list[FileTypes], + prompt: str, + background: Literal["transparent", "opaque", "auto"] | Omit = omit, + input_fidelity: Literal["high", "low"] | Omit = omit, + mask: FileTypes | Omit = omit, + model: str | ImageModel | Omit = omit, + n: int | Omit = omit, + output_compression: int | Omit = omit, + output_format: Literal["png", "jpeg", "webp"] | Omit = omit, + partial_images: int | Omit = omit, + quality: Literal["standard", "low", "medium", "high", "auto"] | Omit = omit, + response_format: Literal["url", "b64_json"] | Omit = omit, + size: Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"] | Omit = omit, + stream: bool | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ImagesResponse | Stream[ImageEditStreamEvent]: + """ + Edit or extend an image using a mask and prompt. Supports dall-e-2 and gpt-image-1. + + Args: + image: Image(s) to edit. + - dall-e-2: Single PNG image, must be square, less than 4MB + - gpt-image-1: Single image or list of up to 16 images (png/webp/jpg), less than 50MB each + Transparent areas indicate where to edit if no mask provided. + + prompt: Text description of desired edits. + - dall-e-2: Max 1000 characters + - gpt-image-1: Max 32000 characters + + background: (gpt-image-1 only) Background transparency control. Options: + - "transparent": Transparent background (requires png or webp output) + - "opaque": Opaque background + - "auto": Model determines best option (default) + + input_fidelity: (gpt-image-1 only, not supported for gpt-image-1-mini) Control how much + the model matches input image style and features, especially facial features. Options: + - "low": Less strict matching (default) + - "high": Stricter matching of input features + + mask: Optional mask image. Must be PNG, same dimensions as image, less than 4MB. + Fully transparent areas (alpha=0) indicate where to edit. If multiple input images + are provided, mask applies to the first image. + + model: Model to use. Options: + - "dall-e-2": Original model, limited features + - "gpt-image-1": Latest model with advanced features (default if gpt-image-1 + specific parameters are used) + + n: Number of variations to generate (1-10). Default 1. + + output_compression: (gpt-image-1 only) Compression level (0-100%) for webp or jpeg + output formats. Default 100. Higher values = better quality, larger file size. + + output_format: (gpt-image-1 only) Output image format. Options: + - "png": PNG format (default), supports transparency + - "jpeg": JPEG format, smaller files, no transparency + - "webp": WebP format, good compression with quality + + partial_images: (gpt-image-1 only) Number of partial images for streaming (0-3). + Set to 0 for single final image. Default returns progressive partial images. + Final image may arrive before all partials if generation completes quickly. + + quality: (gpt-image-1 only) Output quality level. Options: + - "standard": Standard quality + - "low": Lower quality, faster + - "medium": Medium quality + - "high": High quality, slower + - "auto": Model determines best quality + + response_format: Response data format. Options: + - "url": Returns URL (default), expires after 1 hour + - "b64_json": Returns base64-encoded JSON + + size: Output dimensions. Options: + - dall-e-2: "256x256", "512x512", "1024x1024" (default) + - gpt-image-1: Same as dall-e-2 plus "1536x1024", "1024x1536", "auto" + + stream: (gpt-image-1 only) If true, returns Stream[ImageEditStreamEvent] for + progressive image generation. Set partial_images to control streaming behavior. + + user: Unique end-user identifier for abuse monitoring. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ImagesResponse: Contains edited image(s) when stream=False. + Stream[ImageEditStreamEvent]: Streaming events when stream=True (gpt-image-1 only). + + Raises: + BadRequestError: Invalid image format, size, or prompt + """ +``` + +Usage examples: + +```python +from openai import OpenAI +from pathlib import Path + +client = OpenAI() + +# Basic edit with DALL-E 2 +with open("base_image.png", "rb") as image_file, \ + open("mask.png", "rb") as mask_file: + + response = client.images.edit( + image=image_file, + mask=mask_file, + prompt="Add a red hat", + model="dall-e-2", + n=1, + size="1024x1024" + ) + +# Save result +import requests +image_url = response.data[0].url +image_data = requests.get(image_url).content +Path("edited_image.png").write_bytes(image_data) + +# Edit with GPT-Image-1 - multiple images +with open("image1.png", "rb") as img1, \ + open("image2.png", "rb") as img2: + + response = client.images.edit( + image=[img1, img2], # Multiple images for context + prompt="Blend these images together with a sunset background", + model="gpt-image-1", + output_format="webp", + quality="high", + background="auto" + ) + +# GPT-Image-1 with high input fidelity +with open("portrait.png", "rb") as image_file: + response = client.images.edit( + image=image_file, + prompt="Change the background to a beach scene, keep facial features", + model="gpt-image-1", + input_fidelity="high", # Preserve facial features + output_format="png", + size="1536x1024" + ) + +# GPT-Image-1 with transparent background +with open("object.png", "rb") as image_file: + response = client.images.edit( + image=image_file, + prompt="Remove the background", + model="gpt-image-1", + background="transparent", + output_format="png" # Must use png or webp for transparency + ) + +# Streaming edit with GPT-Image-1 +with open("base_image.png", "rb") as image_file: + stream = client.images.edit( + image=image_file, + prompt="Add dramatic lighting", + model="gpt-image-1", + stream=True, + partial_images=3 # Get 3 progressive partial images + ) + + for event in stream: + if event.type == "image": + print(f"Received image chunk: {len(event.data)} bytes") + elif event.type == "done": + print("Final image complete") + +# Compressed output for web use +with open("large_image.png", "rb") as image_file: + response = client.images.edit( + image=image_file, + prompt="Enhance colors", + model="gpt-image-1", + output_format="webp", + output_compression=85, # 85% quality for smaller file size + quality="medium" + ) + +# Edit using transparency (no mask) - works with both models +with open("image_with_transparency.png", "rb") as image_file: + response = client.images.edit( + image=image_file, + prompt="Fill transparent areas with flowers" + ) +``` + +### Create Variations + +Generate variations of an existing image (DALL-E 2 only). + +```python { .api } +def create_variation( + self, + *, + image: FileTypes, + model: str | ImageModel | Omit = omit, + n: int | Omit = omit, + response_format: Literal["url", "b64_json"] | Omit = omit, + size: Literal["256x256", "512x512", "1024x1024"] | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ImagesResponse: + """ + Create variations of an existing image. Only supports dall-e-2. + + Args: + image: Base image for variations. Must be valid PNG, less than 4MB, and square. + + model: Model to use. Only "dall-e-2" is supported for variations. + + n: Number of variations to generate (1-10). Default 1. + + response_format: Output format. + - "url": Returns URL (default), expires after 1 hour + - "b64_json": Returns base64-encoded JSON + + size: Output dimensions. Options: "256x256", "512x512", "1024x1024". + Default "1024x1024". + + user: Unique end-user identifier. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ImagesResponse: Contains variation image(s). + + Raises: + BadRequestError: Invalid image format or size + """ +``` + +Usage example: + +```python +from openai import OpenAI +import requests +from pathlib import Path + +client = OpenAI() + +# Create multiple variations +with open("base_image.png", "rb") as image_file: + response = client.images.create_variation( + image=image_file, + n=3, + size="1024x1024" + ) + +# Save all variations +for i, image in enumerate(response.data): + image_data = requests.get(image.url).content + Path(f"variation_{i+1}.png").write_bytes(image_data) + +# Base64 format +with open("base_image.png", "rb") as image_file: + response = client.images.create_variation( + image=image_file, + response_format="b64_json" + ) + +import base64 +image_data = base64.b64decode(response.data[0].b64_json) +Path("variation.png").write_bytes(image_data) +``` + +## Types + +```python { .api } +from typing import Literal, Union +from pydantic import BaseModel + +class ImagesResponse(BaseModel): + """Response from image endpoints.""" + created: int + data: list[Image] + +class Image(BaseModel): + """Single generated/edited image.""" + url: str | None # Present when response_format="url" + b64_json: str | None # Present when response_format="b64_json" + revised_prompt: str | None # Only for dall-e-3 generations + +class ImageEditStreamEvent(BaseModel): + """Streaming event from images.edit() with stream=True (gpt-image-1 only).""" + type: Literal["image", "done", "error"] + data: bytes | None # Image data for type="image" + error: str | None # Error message for type="error" + +class ImageGenStreamEvent(BaseModel): + """Streaming event from images.generate() with stream=True (gpt-image-1 only).""" + type: Literal["image", "done", "error"] + data: bytes | None # Image data for type="image" + error: str | None # Error message for type="error" + +# Model types +ImageModel = Literal["dall-e-2", "dall-e-3", "gpt-image-1", "gpt-image-1-mini"] + +# File types +FileTypes = Union[ + FileContent, # File-like object + tuple[str | None, FileContent], # (filename, content) + tuple[str | None, FileContent, str | None] # (filename, content, content_type) +] +``` + +## Model Comparison + +| Feature | DALL-E 3 | GPT-Image-1 | DALL-E 2 | +|---------|----------|-------------|----------| +| Generation Quality | Highest | Very High | Good | +| Max Prompt Length (generate) | 4000 chars | N/A | 1000 chars | +| Max Prompt Length (edit) | N/A | 32000 chars | 1000 chars | +| Images per Request (generate) | 1 only | N/A | 1-10 | +| Images per Request (edit) | N/A | Up to 16 input | 1 input | +| Generation Sizes | 1024x1024, 1792x1024, 1024x1792 | N/A | 256x256, 512x512, 1024x1024 | +| Edit Sizes | N/A | 256x256-1536x1024, auto | 256x256-1024x1024 | +| Quality Options | standard, hd | standard, low, medium, high, auto | N/A | +| Style Options | vivid, natural | N/A | N/A | +| Prompt Revision | Yes | N/A | No | +| Image Generation | Yes | No | Yes | +| Image Editing | No | Yes | Yes | +| Variations | No | No | Yes | +| Multiple Input Images | N/A | Yes (up to 16) | No | +| Output Formats | png | png, jpeg, webp | png | +| Transparency Control | N/A | Yes | Limited | +| Input Fidelity Control | N/A | Yes | No | +| Streaming Support | No | Yes | No | +| Compression Control | No | Yes | No | + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Use detailed, specific prompts +response = client.images.generate( + model="dall-e-3", + prompt="A photorealistic portrait of a golden retriever wearing " + "aviator sunglasses, sitting in a red vintage convertible, " + "with a sunny beach in the background, shot on 35mm film" +) + +# 2. Check revised prompts (DALL-E 3) +response = client.images.generate( + model="dall-e-3", + prompt="A dog" +) +print(f"Revised: {response.data[0].revised_prompt}") + +# 3. Use appropriate quality for use case +# Standard for drafts/iterations +response = client.images.generate( + model="dall-e-3", + prompt="Concept art", + quality="standard" +) + +# HD for final production +response = client.images.generate( + model="dall-e-3", + prompt="Final poster design", + quality="hd" +) + +# 4. Handle errors gracefully +from openai import BadRequestError + +try: + response = client.images.generate( + prompt="inappropriate content" + ) +except BadRequestError as e: + if "content_policy_violation" in str(e): + print("Prompt violated content policy") + else: + print(f"Error: {e}") +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def generate_image(): + client = AsyncOpenAI() + + response = await client.images.generate( + model="dall-e-3", + prompt="A futuristic robot", + size="1024x1024" + ) + + return response.data[0].url + +# Run async +image_url = asyncio.run(generate_image()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/index.md b/.tessl/tiles/tessl/pypi-openai/docs/index.md new file mode 100644 index 000000000..9ad8dc886 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/index.md @@ -0,0 +1,2971 @@ +# OpenAI Python Library + +The official Python client library for the OpenAI API, providing comprehensive access to OpenAI's suite of AI models including GPT-4, DALL-E, Whisper, and Embeddings. The library features both synchronous and asynchronous implementations, complete type definitions, streaming support, structured output parsing, and integration with OpenAI's latest features including the Assistants API, Realtime API, and advanced capabilities like function calling and vision inputs. + +## Package Information + +- **Package Name**: openai +- **Language**: Python +- **Installation**: `pip install openai` +- **Python Version**: 3.9+ +- **Official Documentation**: https://platform.openai.com/docs/api-reference + +## Quick Start for Agents + +```python +# Minimal working example - most common agent use case +from openai import OpenAI + +client = OpenAI() # Reads OPENAI_API_KEY from environment + +# Simple completion +response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] +) +print(response.choices[0].message.content) + +# With error handling (production pattern) +from openai import OpenAIError, RateLimitError, APIError + +try: + response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] + ) + content = response.choices[0].message.content +except RateLimitError as e: + # Handle rate limiting with exponential backoff + print(f"Rate limited: {e}") +except APIError as e: + # Handle API errors + print(f"API error: {e}") +except OpenAIError as e: + # Handle all other OpenAI errors + print(f"Error: {e}") +``` + +## Core Imports + +```python +# Essential imports for most use cases +from openai import OpenAI, AsyncOpenAI +from openai import AzureOpenAI, AsyncAzureOpenAI # For Azure + +# Common types and utilities +from typing import Callable, Iterable, Awaitable, Mapping, Literal, AsyncGenerator +import httpx +from openai import ( + Stream, AsyncStream, Client, AsyncClient, + NOT_GIVEN, NotGiven, not_given, Omit, omit, + AssistantEventHandler, AsyncAssistantEventHandler, + HttpxBinaryResponseContent, RequestOptions, Timeout, + APIResponse, AsyncAPIResponse +) +from openai._types import FileTypes +from openai.types.websocket_connection_options import WebsocketConnectionOptions + +# Chat completion types (most commonly used) +from openai.types.chat import ( + ChatCompletion, + ChatCompletionMessage, + ChatCompletionMessageParam, + ChatCompletionToolUnionParam, + ChatCompletionToolChoiceOptionParam, + completion_create_params, +) + +# Pagination types +from openai.pagination import ( + SyncPage, AsyncPage, + SyncCursorPage, AsyncCursorPage, + SyncConversationCursorPage, AsyncConversationCursorPage +) + +# Helpers for audio recording and playback +from openai.helpers import Microphone, LocalAudioPlayer + +# Access all type definitions +from openai import types +# Examples: types.ChatCompletion, types.Embedding, types.FileObject +# Use types.chat.ChatCompletionMessageParam for nested types + +# Error handling imports +from openai import ( + OpenAIError, APIError, APIStatusError, + APITimeoutError, APIConnectionError, APIResponseValidationError, + BadRequestError, AuthenticationError, PermissionDeniedError, + NotFoundError, ConflictError, UnprocessableEntityError, + RateLimitError, InternalServerError, + LengthFinishReasonError, ContentFilterFinishReasonError +) +``` + +## Basic Usage + +```python +from openai import OpenAI + +# Initialize the client (reads OPENAI_API_KEY from environment by default) +client = OpenAI(api_key="your-api-key") # Or omit api_key to use env var + +# Create a chat completion +response = client.chat.completions.create( + model="gpt-4", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello!"} + ] +) + +print(response.choices[0].message.content) + +# Async usage +import asyncio +from openai import AsyncOpenAI + +async def main(): + client = AsyncOpenAI(api_key="your-api-key") + response = await client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] + ) + print(response.choices[0].message.content) + +asyncio.run(main()) +``` + +## Module-Level Configuration (Legacy) + +The library supports a legacy module-level configuration pattern for backward compatibility. You can configure the default client by setting module-level variables: + +```python +import openai + +# API Configuration +openai.api_key = "your-api-key" +openai.organization = "org-xxx" +openai.project = "proj-xxx" +openai.webhook_secret = "whsec_xxx" + +# Network Configuration +openai.base_url = "https://api.openai.com/v1" +openai.timeout = 60.0 # seconds +openai.max_retries = 3 +openai.default_headers = {"Custom-Header": "value"} +openai.default_query = {"custom_param": "value"} +openai.http_client = custom_httpx_client + +# Azure OpenAI Configuration +openai.api_type = "azure" # "openai" or "azure" +openai.api_version = "2024-02-01" +openai.azure_endpoint = "https://your-resource.openai.azure.com" +openai.azure_ad_token = "your-ad-token" +openai.azure_ad_token_provider = lambda: get_token() + +# After setting these, you can use module-level methods: +# (This pattern is deprecated; prefer explicit client instantiation) +``` + +**Note**: The module-level configuration pattern is legacy. For new code, prefer explicit client instantiation: + +```python +from openai import OpenAI + +client = OpenAI( + api_key="your-api-key", + organization="org-xxx", + timeout=60.0 +) +``` + +## Architecture + +The OpenAI Python library is structured around client-resource architecture: + +- **Clients**: `OpenAI`, `AsyncOpenAI`, `AzureOpenAI`, `AsyncAzureOpenAI` - Entry points for API access with configuration +- **Resources**: Organized API endpoints (chat, audio, images, files, etc.) accessible as client attributes +- **Types**: Comprehensive Pydantic models for all requests and responses with full type safety +- **Streaming**: First-class streaming support via `Stream` and `AsyncStream` classes +- **Error Handling**: Structured exception hierarchy for different error types + +The library provides both synchronous and asynchronous implementations for all operations, enabling integration into any Python application architecture. + +## Capabilities + +### Client Initialization + +Initialize OpenAI clients with API credentials and configuration options for both OpenAI and Azure OpenAI services. + +```python { .api } +class OpenAI: + """Synchronous client for OpenAI API.""" + def __init__( + self, + *, + api_key: str | None | Callable[[], str] = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + ): ... + +class AsyncOpenAI: + """Asynchronous client for OpenAI API.""" + def __init__( + self, + *, + api_key: str | None | Callable[[], Awaitable[str]] = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + ): ... + +class AzureOpenAI: + """Synchronous client for Azure OpenAI Service.""" + def __init__( + self, + *, + api_version: str | None = None, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_key: str | None | Callable[[], str] = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: Callable[[], str] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.Client | None = None, + ): ... + +class AsyncAzureOpenAI: + """Asynchronous client for Azure OpenAI Service.""" + def __init__( + self, + *, + api_version: str | None = None, + azure_endpoint: str | None = None, + azure_deployment: str | None = None, + api_key: str | None | Callable[[], Awaitable[str]] = None, + azure_ad_token: str | None = None, + azure_ad_token_provider: Callable[[], Awaitable[str] | str] | None = None, + organization: str | None = None, + project: str | None = None, + webhook_secret: str | None = None, + base_url: str | httpx.URL | None = None, + websocket_base_url: str | httpx.URL | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + max_retries: int = 2, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + http_client: httpx.AsyncClient | None = None, + ): ... + +# Aliases for convenience +Client = OpenAI +AsyncClient = AsyncOpenAI +``` + +[Client Initialization](./client-initialization.md) + +### Chat Completions + +Create conversational responses using OpenAI's language models with support for text, function calling, vision inputs, audio, and structured output parsing. + +```python { .api } +def create( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: str | ChatModel, + audio: dict | Omit = omit, + frequency_penalty: float | Omit = omit, + function_call: str | dict | Omit = omit, + functions: Iterable[dict] | Omit = omit, + logit_bias: dict[str, int] | Omit = omit, + logprobs: bool | Omit = omit, + top_logprobs: int | Omit = omit, + max_completion_tokens: int | Omit = omit, + max_tokens: int | Omit = omit, + metadata: dict[str, str] | Omit = omit, + modalities: list[Literal["text", "audio"]] | Omit = omit, + n: int | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + prediction: dict | Omit = omit, + presence_penalty: float | Omit = omit, + prompt_cache_key: str | Omit = omit, + prompt_cache_retention: Literal["in-memory", "24h"] | Omit = omit, + reasoning_effort: str | Omit = omit, + response_format: completion_create_params.ResponseFormat | Omit = omit, + safety_identifier: str | Omit = omit, + seed: int | Omit = omit, + service_tier: Literal["auto", "default", "flex", "scale", "priority"] | Omit = omit, + stop: str | list[str] | Omit = omit, + store: bool | Omit = omit, + stream: bool | Omit = omit, + stream_options: dict | Omit = omit, + temperature: float | Omit = omit, + tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, + tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, + top_p: float | Omit = omit, + user: str | Omit = omit, + verbosity: Literal["low", "medium", "high"] | Omit = omit, + web_search_options: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, +) -> ChatCompletion | Stream[ChatCompletionChunk]: ... + +def parse( + self, + *, + messages: Iterable[ChatCompletionMessageParam], + model: str | ChatModel, + response_format: Type[BaseModel], + **kwargs +) -> ParsedChatCompletion: ... +``` + +[Chat Completions](./chat-completions.md) + +### Text Completions + +Generate text completions using legacy completion models. + +```python { .api } +def create( + self, + *, + model: str, + prompt: str | list[str] | list[int] | list[list[int]] | None, + best_of: int | Omit = omit, + echo: bool | Omit = omit, + frequency_penalty: float | Omit = omit, + logit_bias: dict[str, int] | Omit = omit, + logprobs: int | Omit = omit, + max_tokens: int | Omit = omit, + n: int | Omit = omit, + presence_penalty: float | Omit = omit, + seed: int | Omit = omit, + stop: str | list[str] | Omit = omit, + stream_options: Optional[dict] | Omit = omit, + stream: bool | Omit = omit, + suffix: str | Omit = omit, + temperature: float | Omit = omit, + top_p: float | Omit = omit, + user: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, +) -> Completion | Stream[Completion]: ... +``` + +[Text Completions](./completions.md) + +### Embeddings + +Create vector embeddings for text inputs to use in semantic search, clustering, and other ML applications. + +```python { .api } +def create( + self, + *, + input: str | list[str] | list[int] | list[list[int]], + model: str | EmbeddingModel, + dimensions: int | Omit = omit, + encoding_format: Literal["float", "base64"] | Omit = omit, + user: str | Omit = omit, + extra_headers: Mapping[str, str] | None = None, + extra_query: Mapping[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, +) -> CreateEmbeddingResponse: ... +``` + +[Embeddings](./embeddings.md) + +### Audio + +Convert audio to text (transcription and translation) and text to speech using Whisper and TTS models. + +```python { .api } +# Transcription +def create( + self, + *, + file: FileTypes, + model: str | AudioModel, + chunking_strategy: Optional[dict] | Omit = omit, + include: list[str] | Omit = omit, + known_speaker_names: list[str] | Omit = omit, + known_speaker_references: list[str] | Omit = omit, + language: str | Omit = omit, + prompt: str | Omit = omit, + response_format: str | Omit = omit, + stream: bool | Omit = omit, + temperature: float | Omit = omit, + timestamp_granularities: list[str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, +) -> Transcription | TranscriptionVerbose | TranscriptionDiarized | Stream[TranscriptionStreamEvent]: ... + +# Translation +def create( + self, + *, + file: FileTypes, + model: str | AudioModel, + prompt: str | Omit = omit, + response_format: str | Omit = omit, + temperature: float | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, +) -> Translation | TranslationVerbose: ... + +# Text-to-Speech +def create( + self, + *, + input: str, + model: str | SpeechModel, + voice: str, + instructions: str | Omit = omit, + response_format: str | Omit = omit, + speed: float | Omit = omit, + stream_format: Literal["sse", "audio"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, +) -> HttpxBinaryResponseContent: ... +``` + +[Audio](./audio.md) + +### Images + +Generate, edit, and create variations of images using DALL-E models. + +```python { .api } +def generate( + self, + *, + prompt: str, + background: Literal["transparent", "opaque", "auto"] | None | Omit = omit, + model: str | ImageModel | None = None, + moderation: Literal["low", "auto"] | None | Omit = omit, + n: int | None = None, + output_compression: int | None | Omit = omit, + output_format: Literal["png", "jpeg", "webp"] | None | Omit = omit, + partial_images: int | None | Omit = omit, + quality: str | None = None, + response_format: str | None = None, + size: str | None = None, + stream: bool | None | Omit = omit, + style: str | None = None, + user: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ImagesResponse: ... + +def edit( + self, + *, + image: FileTypes | list[FileTypes], + prompt: str, + background: Literal["transparent", "opaque", "auto"] | None | Omit = omit, + input_fidelity: Literal["high", "low"] | None | Omit = omit, + mask: FileTypes | None = None, + model: str | ImageModel | None = None, + n: int | None = None, + output_compression: int | None | Omit = omit, + output_format: Literal["png", "jpeg", "webp"] | None | Omit = omit, + partial_images: int | None | Omit = omit, + quality: Literal["standard", "low", "medium", "high", "auto"] | None | Omit = omit, + response_format: str | None = None, + size: str | None = None, + stream: bool | None | Omit = omit, + user: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ImagesResponse: ... + +def create_variation( + self, + *, + image: FileTypes, + model: str | ImageModel | None = None, + n: int | None = None, + response_format: str | None = None, + size: str | None = None, + user: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ImagesResponse: ... +``` + +[Images](./images.md) + +### Videos + +Generate and manipulate videos using video generation models. + +```python { .api } +def create( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: ... + +def create_and_poll( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + poll_interval_ms: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: ... + +def poll( + self, + video_id: str, + *, + poll_interval_ms: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: ... + +def retrieve( + self, + video_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Video: ... + +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Video]: ... + +def delete( + self, + video_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VideoDeleteResponse: ... + +def download_content( + self, + video_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> HttpxBinaryResponseContent: ... + +def remix( + self, + video_id: str, + *, + prompt: str, + seconds: VideoSeconds | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: ... +``` + +[Videos](./videos.md) + +### Files + +Upload and manage files for use with various OpenAI features like Assistants, Fine-tuning, and Batch processing. + +```python { .api } +def create( + self, + *, + file: FileTypes, + purpose: str | FilePurpose, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FileObject: ... + +def retrieve( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FileObject: ... + +def list( + self, + *, + purpose: str | None = None, + limit: int | None = None, + order: str | None = None, + after: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncPage[FileObject]: ... + +def delete( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FileDeleted: ... + +def content( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> HttpxBinaryResponseContent: ... + +def retrieve_content( + self, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> HttpxBinaryResponseContent: ... + +def wait_for_processing( + self, + file_id: str, + *, + poll_interval: float = 5.0, + max_wait: float = 3600.0, +) -> FileObject: ... +``` + +[Files](./files.md) + +### Uploads + +Upload large files in chunks for use with Assistants and other features. Upload parts are managed through the `.parts` subresource. + +```python { .api } +def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: FilePurpose, + expires_after: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Upload: ... + +def complete( + self, + upload_id: str, + *, + part_ids: list[str], + md5: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Upload: ... + +def cancel( + self, + upload_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Upload: ... + +def upload_file_chunked( + self, + *, + file: str | os.PathLike | bytes, + mime_type: str, + purpose: FilePurpose, + filename: str | None = None, + bytes: int | None = None, + part_size: int | None = None, + md5: str | Omit = omit, +) -> Upload: ... + +# Access via client.uploads.parts +def create( + self, + upload_id: str, + *, + data: FileTypes, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> UploadPart: ... +``` + +[Uploads](./uploads.md) + +### Models + +Retrieve information about available models and manage fine-tuned models. + +```python { .api } +def retrieve( + self, + model: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Model: ... + +def list( + self, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncPage[Model]: ... + +def delete( + self, + model: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ModelDeleted: ... +``` + +[Models](./models.md) + +### Fine-tuning + +Create and manage fine-tuning jobs to customize models on your own data. Fine-tuning operations are accessed through the `.jobs` and `.checkpoints` subresources. + +```python { .api } +# Access via client.fine_tuning.jobs +def create( + self, + *, + model: str, + training_file: str, + hyperparameters: dict | None = None, + method: dict | None = None, + integrations: list[dict] | None = None, + seed: int | None = None, + suffix: str | None = None, + validation_file: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FineTuningJob: ... + +def retrieve( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FineTuningJob: ... + +def list( + self, + *, + after: str | None = None, + limit: int | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[FineTuningJob]: ... + +def cancel( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FineTuningJob: ... + +def pause( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FineTuningJob: ... + +def resume( + self, + fine_tuning_job_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> FineTuningJob: ... + +def list_events( + self, + fine_tuning_job_id: str, + *, + after: str | None = None, + limit: int | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[FineTuningJobEvent]: ... + +# Access via client.fine_tuning.checkpoints +def list( + self, + fine_tuning_job_id: str, + *, + after: str | None = None, + limit: int | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[FineTuningJobCheckpoint]: ... +``` + +[Fine-tuning](./fine-tuning.md) + +### Moderations + +Check content against OpenAI's usage policies to detect potentially harmful content. + +```python { .api } +def create( + self, + *, + input: str | list[str], + model: str | ModerationModel | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ModerationCreateResponse: ... +``` + +[Moderations](./moderations.md) + +### Batch Processing + +Submit batch requests for asynchronous processing of multiple API calls. + +```python { .api } +def create( + self, + *, + completion_window: str, + endpoint: str, + input_file_id: str, + metadata: dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Batch: ... + +def retrieve( + self, + batch_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Batch: ... + +def list( + self, + *, + after: str | None = None, + limit: int | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[Batch]: ... + +def cancel( + self, + batch_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Batch: ... +``` + +[Batch Processing](./batches.md) + +### Vector Stores + +Create and manage vector stores for semantic search and retrieval with the Assistants API. Vector store files are managed through the `.files` subresource, and file batches through the `.file_batches` subresource. + +```python { .api } +def create( + self, + *, + file_ids: list[str] | None = None, + name: str | None = None, + expires_after: dict | None = None, + chunking_strategy: dict | None = None, + metadata: dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStore: ... + +def retrieve( + self, + vector_store_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStore: ... + +def update( + self, + vector_store_id: str, + *, + name: str | None = None, + expires_after: dict | None = None, + metadata: dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStore: ... + +def list( + self, + *, + after: str | None = None, + before: str | None = None, + limit: int | None = None, + order: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[VectorStore]: ... + +def delete( + self, + vector_store_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreDeleted: ... + +def search( + self, + vector_store_id: str, + *, + query: str, + limit: int | None = None, + filter: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreSearchResponse: ... + +# Access via client.vector_stores.files +def create( + self, + vector_store_id: str, + *, + file_id: str, + chunking_strategy: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreFile: ... + +def retrieve( + self, + vector_store_id: str, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreFile: ... + +def list( + self, + vector_store_id: str, + *, + after: str | None = None, + before: str | None = None, + filter: str | None = None, + limit: int | None = None, + order: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[VectorStoreFile]: ... + +def delete( + self, + vector_store_id: str, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreFileDeleted: ... + +# Access via client.vector_stores.file_batches +def create( + self, + vector_store_id: str, + *, + file_ids: list[str], + chunking_strategy: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreFileBatch: ... + +def retrieve( + self, + vector_store_id: str, + batch_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreFileBatch: ... + +def cancel( + self, + vector_store_id: str, + batch_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> VectorStoreFileBatch: ... + +def list_files( + self, + vector_store_id: str, + batch_id: str, + *, + after: str | None = None, + before: str | None = None, + filter: str | None = None, + limit: int | None = None, + order: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[VectorStoreFile]: ... +``` + +[Vector Stores](./vector-stores.md) + +### Assistants API (Beta) + +Build AI assistants with advanced capabilities including code interpreter, file search, and function calling. + +```python { .api } +def create( + self, + *, + model: str, + description: str | None = None, + instructions: str | None = None, + metadata: dict[str, str] | None = None, + name: str | None = None, + response_format: dict | None = None, + temperature: float | None = None, + tool_resources: dict | None = None, + tools: list[dict] | None = None, + top_p: float | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Assistant: ... + +def retrieve( + self, + assistant_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Assistant: ... + +def update( + self, + assistant_id: str, + *, + description: str | None = None, + instructions: str | None = None, + metadata: dict[str, str] | None = None, + model: str | None = None, + name: str | None = None, + response_format: dict | None = None, + temperature: float | None = None, + tool_resources: dict | None = None, + tools: list[dict] | None = None, + top_p: float | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Assistant: ... + +def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Assistant]: ... + +def delete( + self, + assistant_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> AssistantDeleted: ... +``` + +[Assistants API](./assistants.md) + +### Threads and Messages (Beta) + +Create conversational threads and manage messages within the Assistants API. + +```python { .api } +# Threads +def create( + self, + *, + messages: list[dict] | None = None, + metadata: dict[str, str] | None = None, + tool_resources: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Thread: ... + +def retrieve( + self, + thread_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Thread: ... + +def update( + self, + thread_id: str, + *, + metadata: dict[str, str] | None = None, + tool_resources: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Thread: ... + +def delete( + self, + thread_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ThreadDeleted: ... + +def create_and_run( + self, + *, + assistant_id: str, + instructions: str | None = None, + metadata: dict[str, str] | None = None, + model: str | None = None, + thread: dict | None = None, + tools: list[dict] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def create_and_run_poll( + self, + *, + assistant_id: str, + poll_interval_ms: int | None = None, + instructions: str | None = None, + metadata: dict[str, str] | None = None, + model: str | None = None, + thread: dict | None = None, + tools: list[dict] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def create_and_run_stream( + self, + *, + assistant_id: str, + event_handler: AssistantEventHandler | None = None, + instructions: str | None = None, + metadata: dict[str, str] | None = None, + model: str | None = None, + thread: dict | None = None, + tools: list[dict] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> AssistantStreamManager: ... + +# Messages +def create( + self, + thread_id: str, + *, + role: str, + content: str | list[dict], + attachments: list[dict] | None = None, + metadata: dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Message: ... + +def retrieve( + self, + thread_id: str, + message_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Message: ... + +def update( + self, + thread_id: str, + message_id: str, + *, + metadata: dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Message: ... + +def list( + self, + thread_id: str, + *, + after: str | None = None, + before: str | None = None, + limit: int | None = None, + order: str | None = None, + run_id: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[Message]: ... + +def delete( + self, + thread_id: str, + message_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> MessageDeleted: ... +``` + +[Threads and Messages](./threads-messages.md) + +### Runs (Beta) + +Execute assistants on threads and handle tool calls. Run steps are managed through the `.steps` subresource. + +```python { .api } +def create( + self, + thread_id: str, + *, + assistant_id: str, + additional_instructions: str | None = None, + additional_messages: list[dict] | None = None, + instructions: str | None = None, + max_completion_tokens: int | None = None, + max_prompt_tokens: int | None = None, + metadata: dict[str, str] | None = None, + model: str | None = None, + parallel_tool_calls: bool | None = None, + response_format: dict | None = None, + stream: bool | None = None, + temperature: float | None = None, + tool_choice: str | dict | None = None, + tools: list[dict] | None = None, + top_p: float | None = None, + truncation_strategy: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def retrieve( + self, + thread_id: str, + run_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def update( + self, + thread_id: str, + run_id: str, + *, + metadata: dict[str, str] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def list( + self, + thread_id: str, + *, + after: str | None = None, + before: str | None = None, + limit: int | None = None, + order: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[Run]: ... + +def cancel( + self, + thread_id: str, + run_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def submit_tool_outputs( + self, + thread_id: str, + run_id: str, + *, + tool_outputs: list[dict], + stream: bool | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Run: ... + +def stream( + self, + thread_id: str, + *, + assistant_id: str, + event_handler: AssistantEventHandler | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> AssistantStreamManager: ... + +# Access via client.beta.threads.runs.steps +def retrieve( + self, + thread_id: str, + run_id: str, + step_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> RunStep: ... + +def list( + self, + thread_id: str, + run_id: str, + *, + after: str | None = None, + before: str | None = None, + limit: int | None = None, + order: str | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> SyncCursorPage[RunStep]: ... +``` + +[Runs](./runs.md) + +### ChatKit (Beta) + +Simplified, high-level interface for building chat applications with session and thread management. + +```python { .api } +def create( + self, + *, + user: str, + workflow: ChatSessionWorkflowParam, + chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, + expires_after: ChatSessionExpiresAfterParam | Omit = omit, + rate_limits: ChatSessionRateLimitsParam | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ChatSession: ... +``` + +[ChatKit](./chatkit.md) + +### Realtime API (Beta) + +WebSocket-based realtime communication for low-latency conversational AI experiences. Realtime client secrets and calls are managed through the `.client_secrets` and `.calls` subresources. + +```python { .api } +def connect( + self, + *, + call_id: str | Omit = omit, + model: str | Omit = omit, + extra_query: dict[str, object] = {}, + extra_headers: dict[str, str] = {}, + websocket_connection_options: WebsocketConnectionOptions = {}, +) -> RealtimeConnectionManager: ... + +# Access via client.realtime.client_secrets +def create( + self, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ClientSecret: ... + +# Access via client.realtime.calls +def create( + self, + *, + model: str, + call_config: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Call: ... + +def retrieve( + self, + call_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Call: ... + +def update( + self, + call_id: str, + *, + call_config: dict | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Call: ... + +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Call]: ... +``` + +[Realtime API](./realtime.md) + +### Responses API + +Create responses with advanced tool support including computer use, file search, and code patching. Response input items and tokens are managed through the `.input_items` and `.input_tokens` subresources. + +```python { .api } +def create( + self, + *, + model: str, + input: dict | list[dict], + instructions: str | None = None, + metadata: dict[str, str] | None = None, + parallel_tool_calls: bool | None = None, + reasoning_effort: str | None = None, + store: bool | None = None, + stream: bool | None = None, + temperature: float | None = None, + tool_choice: str | dict | None = None, + tools: list[dict] | None = None, + top_p: float | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Response: ... + +def stream( + self, + *, + model: str, + input: dict | list[dict], + instructions: str | None = None, + metadata: dict[str, str] | None = None, + parallel_tool_calls: bool | None = None, + reasoning_effort: str | None = None, + store: bool | None = None, + temperature: float | None = None, + tool_choice: str | dict | None = None, + tools: list[dict] | None = None, + top_p: float | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Stream[ResponseStreamEvent]: ... + +def parse( + self, + *, + model: str, + input: dict | list[dict], + response_format: Type[BaseModel], + instructions: str | None = None, + metadata: dict[str, str] | None = None, + parallel_tool_calls: bool | None = None, + reasoning_effort: str | None = None, + store: bool | None = None, + temperature: float | None = None, + tool_choice: str | dict | None = None, + tools: list[dict] | None = None, + top_p: float | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ParsedResponse: ... + +def retrieve( + self, + response_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Response: ... + +def delete( + self, + response_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ResponseDeleted: ... + +def cancel( + self, + response_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> Response: ... + +# Access via client.responses.input_items +def create( + self, + response_id: str, + *, + type: str, + content: str | list[dict] | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ResponseInputItem: ... + +def delete( + self, + response_id: str, + item_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ResponseInputItemDeleted: ... + +# Access via client.responses.input_tokens +def create( + self, + response_id: str, + *, + token: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ResponseInputToken: ... + +def delete( + self, + response_id: str, + token_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | None = None, +) -> ResponseInputTokenDeleted: ... +``` + +[Responses](./responses.md) + +### Evaluations + +Create and manage evaluations to test model performance with custom testing criteria. Evaluation runs are managed through the `.runs` subresource. + +```python { .api } +def create( + self, + *, + data_source_config: dict, + testing_criteria: Iterable[dict], + metadata: dict[str, str] | None | Omit = omit, + name: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Eval: ... + +def retrieve( + self, + eval_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Eval: ... + +def update( + self, + eval_id: str, + *, + metadata: dict[str, str] | None | Omit = omit, + name: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Eval: ... + +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + order_by: Literal["created_at", "updated_at"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Eval]: ... + +def delete( + self, + eval_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> EvalDeleteResponse: ... + +# Access via client.evals.runs +def create( + self, + eval_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> EvalRun: ... + +def retrieve( + self, + eval_id: str, + run_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> EvalRun: ... + +def list( + self, + eval_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[EvalRun]: ... +``` + +[Evaluations](./evals.md) + +### Conversations + +Create and manage conversations for structured multi-turn interactions. Conversation items are managed through the `.items` subresource. + +```python { .api } +def create( + self, + *, + items: Iterable[dict] | None | Omit = omit, + metadata: dict[str, str] | None | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: ... + +def retrieve( + self, + conversation_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: ... + +def update( + self, + conversation_id: str, + *, + metadata: dict[str, str] | None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Conversation: ... + +def delete( + self, + conversation_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationDeleted: ... + +# Access via client.conversations.items +def create( + self, + conversation_id: str, + *, + type: str, + content: str | list[dict] | None = None, + metadata: dict[str, str] | None | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationItem: ... + +def retrieve( + self, + conversation_id: str, + item_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationItem: ... + +def update( + self, + conversation_id: str, + item_id: str, + *, + content: str | list[dict] | None = None, + metadata: dict[str, str] | None | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationItem: ... + +def list( + self, + conversation_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[ConversationItem]: ... + +def delete( + self, + conversation_id: str, + item_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ConversationItemDeleted: ... +``` + +[Conversations](./conversations.md) + +### Containers + +Create and manage isolated file storage containers for organizing files. Container files are managed through the `.files` subresource. + +```python { .api } +def create( + self, + *, + name: str, + expires_after: dict | Omit = omit, + file_ids: list[str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Container: ... + +def retrieve( + self, + container_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Container: ... + +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Container]: ... + +def delete( + self, + container_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> None: ... + +# Access via client.containers.files +def create( + self, + container_id: str, + *, + file: FileTypes, + purpose: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ContainerFile: ... + +def retrieve( + self, + container_id: str, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ContainerFile: ... + +def list( + self, + container_id: str, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[ContainerFile]: ... + +def delete( + self, + container_id: str, + file_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ContainerFileDeleted: ... +``` + +[Containers](./containers.md) + +### Webhooks + +Verify and handle webhook events from OpenAI for asynchronous notifications. + +```python { .api } +def verify_signature( + payload: str | bytes, + headers: dict[str, str] | list[tuple[str, str]], + *, + secret: str | None = None, + tolerance: int = 300 +) -> None: ... + +def unwrap( + payload: str | bytes, + headers: dict[str, str] | list[tuple[str, str]], + *, + secret: str | None = None +) -> UnwrapWebhookEvent: ... +``` + +[Webhooks](./webhooks.md) + +## Response Wrappers + +All resources support response wrapper patterns for accessing raw HTTP responses or streaming responses without loading them into memory. + +### Raw Response Access + +Access the underlying `httpx.Response` object for any API call: + +```python +from openai import OpenAI + +client = OpenAI() + +# Use .with_raw_response prefix +response = client.with_raw_response.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Hello!"}] +) + +# Access parsed response +completion = response.parsed + +# Access raw HTTP response +http_response = response.http_response +print(f"Status: {http_response.status_code}") +print(f"Headers: {http_response.headers}") +print(f"Raw content: {http_response.content}") +``` + +Available on all resources: + +```python +client.with_raw_response.chat.completions.create(...) +client.with_raw_response.audio.transcriptions.create(...) +client.with_raw_response.images.generate(...) +# ... and all other resources +``` + +### Streaming Response Access + +Stream responses without loading them into memory, useful for large responses: + +```python +response = client.with_streaming_response.files.content("file-abc123") + +# Stream chunks +for chunk in response.iter_bytes(): + process_chunk(chunk) + +# Or write directly to file +with open("output.txt", "wb") as f: + for chunk in response.iter_bytes(): + f.write(chunk) +``` + +Available on all resources: + +```python +client.with_streaming_response.chat.completions.create(...) +client.with_streaming_response.files.content(...) +# ... and all other resources +``` + +Both patterns work with async clients: + +```python +from openai import AsyncOpenAI + +client = AsyncOpenAI() + +# Raw response (async) +response = await client.with_raw_response.chat.completions.create(...) + +# Streaming response (async) +response = await client.with_streaming_response.files.content("file-abc123") +async for chunk in response.iter_bytes(): + process_chunk(chunk) +``` + +## Error Handling + +The library provides a comprehensive exception hierarchy for handling different error scenarios: + +```python { .api } +class OpenAIError(Exception): + """Base exception for all OpenAI errors.""" + +class APIError(OpenAIError): + """Base for API-related errors.""" + +class APIStatusError(APIError): + """HTTP status code errors (4xx, 5xx).""" + status_code: int + response: httpx.Response + body: object + +class APITimeoutError(APIError): + """Request timeout errors.""" + +class APIConnectionError(APIError): + """Connection errors.""" + +class APIResponseValidationError(APIError): + """Response validation errors.""" + +class BadRequestError(APIStatusError): + """400 Bad Request.""" + +class AuthenticationError(APIStatusError): + """401 Authentication error.""" + +class PermissionDeniedError(APIStatusError): + """403 Permission denied.""" + +class NotFoundError(APIStatusError): + """404 Not found.""" + +class ConflictError(APIStatusError): + """409 Conflict.""" + +class UnprocessableEntityError(APIStatusError): + """422 Unprocessable Entity.""" + +class RateLimitError(APIStatusError): + """429 Rate limit exceeded.""" + +class InternalServerError(APIStatusError): + """500+ Server errors.""" + +class LengthFinishReasonError(OpenAIError): + """Raised when completion stops due to reaching max tokens.""" + +class ContentFilterFinishReasonError(OpenAIError): + """Raised when completion stops due to content filtering.""" + +class InvalidWebhookSignatureError(OpenAIError): + """Raised when webhook signature verification fails.""" +``` + +### Production Error Handling Patterns + +```python +from openai import OpenAI, OpenAIError, RateLimitError, APIError, APIConnectionError +import time + +client = OpenAI() + +# Pattern 1: Retry with exponential backoff for rate limits +def create_completion_with_retry(messages, max_retries=3): + for attempt in range(max_retries): + try: + return client.chat.completions.create( + model="gpt-4", + messages=messages + ) + except RateLimitError as e: + if attempt == max_retries - 1: + raise + wait_time = (2 ** attempt) + 1 # Exponential backoff + print(f"Rate limited. Retrying in {wait_time}s...") + time.sleep(wait_time) + except APIConnectionError as e: + if attempt == max_retries - 1: + raise + wait_time = 2 ** attempt + print(f"Connection error. Retrying in {wait_time}s...") + time.sleep(wait_time) + +# Pattern 2: Handle all error types +def safe_completion(messages): + try: + response = client.chat.completions.create( + model="gpt-4", + messages=messages + ) + return response.choices[0].message.content + except RateLimitError as e: + # Handle rate limiting - implement backoff + return f"Rate limited: {e}" + except APIConnectionError as e: + # Handle connection issues - check network + return f"Connection error: {e}" + except APIError as e: + # Handle general API errors + return f"API error: {e}" + except OpenAIError as e: + # Catch all other OpenAI errors + return f"OpenAI error: {e}" + except Exception as e: + # Catch unexpected errors + return f"Unexpected error: {e}" + +# Pattern 3: Check finish reason +response = client.chat.completions.create( + model="gpt-4", + messages=[{"role": "user", "content": "Write a long story"}], + max_tokens=100 +) + +finish_reason = response.choices[0].finish_reason +if finish_reason == "length": + print("Response truncated - increase max_tokens") +elif finish_reason == "content_filter": + print("Content filtered - adjust prompt") +elif finish_reason == "stop": + print("Completed normally") +``` + +## Common Types + +```python { .api } +# Sentinel values for omitted parameters +NOT_GIVEN: Omit # Sentinel indicating parameter was not provided +not_given: Omit # Alias for NOT_GIVEN (lowercase convention) + +# Omit type for optional API parameters +# Use Omit type with omit default value instead of None for optional parameters +# This distinguishes between "parameter not provided" vs "parameter set to null" +Omit: TypeAlias # Type for omittable parameters +omit: Omit # Sentinel value for omitted parameters + +# Type utilities +NoneType: Type[None] # Used for response casting when no response expected + +# Common type aliases +# File upload types - can be specified in multiple formats: +# - Raw file content (bytes, file object, etc.) +# - (filename, content) tuple +# - (filename, content, mime_type) tuple +FileTypes = Union[ + FileContent, # Just the file bytes/buffer + Tuple[Optional[str], FileContent], # (filename, content) + Tuple[Optional[str], FileContent, Optional[str]] # (filename, content, mime_type) +] + +# Timeout can be a float (seconds) or an httpx.Timeout object for fine-grained control +Timeout = Union[float, httpx.Timeout] + +# Azure AD Token Provider +# Callable that returns Azure AD tokens for authentication with Azure OpenAI +# Can be sync or async function +AzureADTokenProvider = Callable[[], str] | Callable[[], Awaitable[str]] + +# Default configuration constants +DEFAULT_TIMEOUT: httpx.Timeout # Default timeout: 600s total, 5s connect +DEFAULT_MAX_RETRIES: int # Default maximum retries: 2 +DEFAULT_CONNECTION_LIMITS: httpx.Limits # Default connection limits: 1000 max connections, 100 keepalive + +# Type aliases for specific domains +VideoModel = Literal["sora-2", "sora-2-pro"] +VideoSeconds = int # Duration in seconds +VideoSize = str # e.g., "720x1280" + +# Request configuration +class RequestOptions(TypedDict, total=False): + """Options for individual API requests.""" + extra_headers: dict[str, str] + extra_query: dict[str, object] + extra_body: dict[str, object] + timeout: float | httpx.Timeout + +# WebSocket configuration for Realtime API +class WebsocketConnectionOptions(TypedDict, total=False): + """WebSocket connection options for realtime API connections.""" + extensions: Sequence[ClientExtensionFactory] | None # List of supported extensions + subprotocols: Sequence[Subprotocol] | None # List of supported subprotocols + compression: str | None # Compression setting ("permessage-deflate" enabled by default, None to disable) + max_size: int | None # Maximum size of incoming messages in bytes + max_queue: int | None | tuple[int | None, int | None] # High-water mark of receive buffer + write_limit: int | tuple[int, int | None] # High-water mark of write buffer in bytes + +# Response wrappers +class HttpxBinaryResponseContent: + """Binary response content for audio, images, etc.""" + content: bytes + response: httpx.Response + def read(self) -> bytes: ... + def write_to_file(self, file: str | os.PathLike) -> None: ... + +# Pagination types +class SyncPage[T]: + """Standard pagination.""" + data: list[T] + object: str + def __iter__(self) -> Iterator[T]: ... + def __next__(self) -> T: ... + +class AsyncPage[T]: + """Async pagination.""" + data: list[T] + object: str + def __aiter__(self) -> AsyncIterator[T]: ... + def __anext__(self) -> T: ... + +class SyncCursorPage[T]: + """Cursor-based pagination.""" + data: list[T] + has_more: bool + def __iter__(self) -> Iterator[T]: ... + def __next__(self) -> T: ... + +class AsyncCursorPage[T]: + """Async cursor-based pagination.""" + data: list[T] + has_more: bool + def __aiter__(self) -> AsyncIterator[T]: ... + def __anext__(self) -> T: ... + +class SyncConversationCursorPage[T]: + """Conversation cursor-based pagination.""" + data: list[T] + has_more: bool + last_id: str | None + def __iter__(self) -> Iterator[T]: ... + def __next__(self) -> T: ... + +class AsyncConversationCursorPage[T]: + """Async conversation cursor-based pagination.""" + data: list[T] + has_more: bool + last_id: str | None + def __aiter__(self) -> AsyncIterator[T]: ... + def __anext__(self) -> T: ... + +# Streaming types +class Stream[T]: + """Synchronous streaming.""" + def __iter__(self) -> Iterator[T]: ... + def __next__(self) -> T: ... + def __enter__(self) -> Stream[T]: ... + def __exit__(self, *args) -> None: ... + def close(self) -> None: ... + +class AsyncStream[T]: + """Asynchronous streaming.""" + def __aiter__(self) -> AsyncIterator[T]: ... + def __anext__(self) -> T: ... + async def __aenter__(self) -> AsyncStream[T]: ... + async def __aexit__(self, *args) -> None: ... + async def close(self) -> None: ... +``` + +## Helper Utilities + +```python { .api } +# Package Information +__title__: str # Package name ("openai") +VERSION: str # Library version string +__version__: str # Library version (same as VERSION) + +# Pydantic Base Model +BaseModel # Base class for creating custom Pydantic models (from pydantic import BaseModel) + +# HTTP Client Classes +class DefaultHttpxClient: + """Default synchronous httpx client with connection pooling.""" + +class DefaultAsyncHttpxClient: + """Default asynchronous httpx client with connection pooling.""" + +class DefaultAioHttpClient: + """Alternative async HTTP client using aiohttp.""" + +# Additional Type Exports +Transport: type # HTTP transport type (httpx.BaseTransport) +ProxiesTypes: type # Proxy configuration type + +# File and Function Utilities +def file_from_path(path: str) -> FileTypes: + """ + Load a file from filesystem path for upload to OpenAI API. + + Args: + path: Filesystem path to the file + + Returns: + FileTypes: A (filename, content, mime_type) tuple suitable for API upload + """ + +def pydantic_function_tool( + model: Type[BaseModel], + *, + name: str | None = None, + description: str | None = None +) -> ChatCompletionToolParam: + """ + Create a function tool definition from a Pydantic model for use with chat completions. + + Args: + model: Pydantic model class defining the function parameters schema + name: Optional function name (defaults to model class name in snake_case) + description: Optional function description (defaults to model docstring) + + Returns: + ChatCompletionToolParam: A tool definition dict with 'type' and 'function' keys. + Type from openai.types.chat module. + Can be passed to chat.completions.create(tools=[...]) + """ + +class AssistantEventHandler: + """Base class for handling assistant streaming events.""" + def on_event(self, event) -> None: ... + def on_run_step_created(self, run_step) -> None: ... + def on_run_step_done(self, run_step) -> None: ... + def on_tool_call_created(self, tool_call) -> None: ... + def on_tool_call_done(self, tool_call) -> None: ... + def on_message_created(self, message) -> None: ... + def on_message_done(self, message) -> None: ... + def on_text_created(self, text) -> None: ... + def on_text_delta(self, delta, snapshot) -> None: ... + def on_text_done(self, text) -> None: ... + +class AsyncAssistantEventHandler: + """Base class for handling assistant streaming events asynchronously.""" + async def on_event(self, event) -> None: ... + async def on_run_step_created(self, run_step) -> None: ... + async def on_run_step_done(self, run_step) -> None: ... + async def on_tool_call_created(self, tool_call) -> None: ... + async def on_tool_call_done(self, tool_call) -> None: ... + async def on_message_created(self, message) -> None: ... + async def on_message_done(self, message) -> None: ... + async def on_text_created(self, text) -> None: ... + async def on_text_delta(self, delta, snapshot) -> None: ... + async def on_text_done(self, text) -> None: ... + +# Audio Recording Helper +class Microphone(Generic[DType]): + """ + Microphone helper for recording audio input from default audio device. + + Requires optional dependencies: numpy, sounddevice + Install with: pip install openai[voice_helpers] + + Type Parameters: + DType: numpy dtype for audio data (default: np.int16) + """ + def __init__( + self, + channels: int = 1, + dtype: Type[DType] = np.int16, + should_record: Callable[[], bool] | None = None, + timeout: float | None = None, + ): + """ + Initialize microphone for recording. + + Args: + channels: Number of audio channels (1 for mono, 2 for stereo) + dtype: Numpy data type for audio samples + should_record: Optional callback function that returns True while recording should continue + timeout: Maximum recording duration in seconds (None for unlimited) + """ + + async def record( + self, + return_ndarray: bool | None = False + ) -> npt.NDArray[DType] | FileTypes: + """ + Record audio from microphone. + + Args: + return_ndarray: If True, return numpy array; if False, return WAV file tuple + + Returns: + Either a numpy array of audio samples or FileTypes tuple (filename, buffer, mime_type) + suitable for passing to OpenAI API methods. + """ + +# Audio Playback Helper +class LocalAudioPlayer: + """ + Local audio player for playing audio content through default audio device. + + Requires optional dependencies: numpy, sounddevice + Install with: pip install openai[voice_helpers] + + The player uses a fixed sample rate of 24000 Hz, 1 channel (mono), and float32 dtype. + """ + def __init__( + self, + should_stop: Callable[[], bool] | None = None, + ): + """ + Initialize audio player. + + Args: + should_stop: Optional callback function that returns True to stop playback + """ + + async def play( + self, + input: npt.NDArray[np.int16] | npt.NDArray[np.float32] | HttpxBinaryResponseContent | AsyncStreamedBinaryAPIResponse | StreamedBinaryAPIResponse + ) -> None: + """ + Play audio data through local audio device. + + Args: + input: Audio data as numpy array (int16 or float32) or response content from TTS API + """ + + async def play_stream( + self, + buffer_stream: AsyncGenerator[npt.NDArray[np.float32] | npt.NDArray[np.int16] | None, None] + ) -> None: + """ + Stream and play audio data as it arrives. + + Useful for playing streaming audio responses from realtime API. + + Args: + buffer_stream: Async generator yielding audio buffers (numpy arrays) or None to signal completion + """ +``` + +## Agent Integration Patterns + +### Pattern 1: Simple Question-Answer Agent + +```python +from openai import OpenAI, OpenAIError + +class SimpleAgent: + def __init__(self, api_key: str | None = None): + self.client = OpenAI(api_key=api_key) + self.model = "gpt-4" + + def ask(self, question: str) -> str: + """Ask a single question and get an answer.""" + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[{"role": "user", "content": question}] + ) + return response.choices[0].message.content + except OpenAIError as e: + return f"Error: {e}" + +# Usage +agent = SimpleAgent() +answer = agent.ask("What is the capital of France?") +print(answer) +``` + +### Pattern 2: Conversational Agent with Memory + +```python +from openai import OpenAI +from typing import List, Dict + +class ConversationalAgent: + def __init__(self, system_prompt: str, api_key: str | None = None): + self.client = OpenAI(api_key=api_key) + self.model = "gpt-4" + self.messages: List[Dict[str, str]] = [ + {"role": "system", "content": system_prompt} + ] + + def chat(self, user_message: str) -> str: + """Send a message and maintain conversation history.""" + self.messages.append({"role": "user", "content": user_message}) + + response = self.client.chat.completions.create( + model=self.model, + messages=self.messages + ) + + assistant_message = response.choices[0].message.content + self.messages.append({"role": "assistant", "content": assistant_message}) + + return assistant_message + + def reset(self): + """Clear conversation history (keep system prompt).""" + self.messages = [self.messages[0]] + +# Usage +agent = ConversationalAgent("You are a helpful Python expert.") +print(agent.chat("How do I read a file?")) +print(agent.chat("And how do I write to it?")) # Maintains context +``` + +### Pattern 3: Function-Calling Agent + +```python +from openai import OpenAI +import json + +class FunctionAgent: + def __init__(self, api_key: str | None = None): + self.client = OpenAI(api_key=api_key) + self.model = "gpt-4" + + # Define available functions + self.tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get current weather for a location", + "parameters": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "City name"}, + "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} + }, + "required": ["location"] + } + } + } + ] + + def execute_function(self, name: str, arguments: str) -> str: + """Execute the actual function call.""" + args = json.loads(arguments) + + if name == "get_weather": + # Implement actual weather lookup + location = args["location"] + unit = args.get("unit", "celsius") + return f"The weather in {location} is 22°{unit[0].upper()}" + + return f"Unknown function: {name}" + + def run(self, user_query: str) -> str: + """Run agent with function calling capability.""" + messages = [{"role": "user", "content": user_query}] + + # Initial request + response = self.client.chat.completions.create( + model=self.model, + messages=messages, + tools=self.tools, + tool_choice="auto" + ) + + response_message = response.choices[0].message + messages.append(response_message) + + # Handle function calls + if response_message.tool_calls: + for tool_call in response_message.tool_calls: + function_name = tool_call.function.name + function_args = tool_call.function.arguments + + # Execute function + function_response = self.execute_function(function_name, function_args) + + # Add function response to messages + messages.append({ + "role": "tool", + "tool_call_id": tool_call.id, + "content": function_response + }) + + # Get final response + final_response = self.client.chat.completions.create( + model=self.model, + messages=messages + ) + return final_response.choices[0].message.content + + return response_message.content + +# Usage +agent = FunctionAgent() +result = agent.run("What's the weather in London?") +print(result) +``` + +### Pattern 4: Structured Output Agent + +```python +from openai import OpenAI +from pydantic import BaseModel +from typing import List + +class Task(BaseModel): + """A single task.""" + title: str + description: str + priority: int # 1-5 + +class TaskList(BaseModel): + """A list of tasks.""" + tasks: List[Task] + +class StructuredAgent: + def __init__(self, api_key: str | None = None): + self.client = OpenAI(api_key=api_key) + self.model = "gpt-4" + + def extract_tasks(self, text: str) -> TaskList: + """Extract structured task list from text.""" + completion = self.client.beta.chat.completions.parse( + model=self.model, + messages=[ + {"role": "system", "content": "Extract tasks from the user's text."}, + {"role": "user", "content": text} + ], + response_format=TaskList + ) + + return completion.choices[0].message.parsed + +# Usage +agent = StructuredAgent() +text = "I need to finish the report by Friday, call the client tomorrow, and review the code this afternoon." +tasks = agent.extract_tasks(text) + +for task in tasks.tasks: + print(f"[P{task.priority}] {task.title}: {task.description}") +``` + +### Pattern 5: Streaming Agent + +```python +from openai import OpenAI +from typing import Generator + +class StreamingAgent: + def __init__(self, api_key: str | None = None): + self.client = OpenAI(api_key=api_key) + self.model = "gpt-4" + + def stream_response(self, prompt: str) -> Generator[str, None, None]: + """Stream response token by token.""" + stream = self.client.chat.completions.create( + model=self.model, + messages=[{"role": "user", "content": prompt}], + stream=True + ) + + for chunk in stream: + if chunk.choices[0].delta.content: + yield chunk.choices[0].delta.content + + def chat_stream(self, prompt: str) -> str: + """Chat with streaming, return complete response.""" + full_response = [] + for chunk in self.stream_response(prompt): + print(chunk, end="", flush=True) + full_response.append(chunk) + print() # Newline after stream + return "".join(full_response) + +# Usage +agent = StreamingAgent() +response = agent.chat_stream("Tell me a short story about AI.") +``` + +## Troubleshooting + +### Common Issues and Solutions + +#### Issue: "APIError: The api_key client option must be set" +**Solution**: Set the OPENAI_API_KEY environment variable or pass api_key explicitly: +```python +# Option 1: Environment variable +import os +os.environ["OPENAI_API_KEY"] = "your-key" +client = OpenAI() + +# Option 2: Explicit parameter +client = OpenAI(api_key="your-key") +``` + +#### Issue: Rate limiting (429 errors) +**Solution**: Implement exponential backoff retry logic (see error handling patterns above). + +#### Issue: "InvalidRequestError: This model's maximum context length is X tokens" +**Solution**: Reduce message history or use a model with larger context: +```python +# Truncate old messages +max_messages = 10 +messages = messages[-max_messages:] + +# Or use a larger context model +model = "gpt-4-turbo" # 128K context +``` + +#### Issue: Streaming responses cut off +**Solution**: Check finish_reason and handle properly: +```python +stream = client.chat.completions.create(model="gpt-4", messages=messages, stream=True) +for chunk in stream: + if chunk.choices[0].finish_reason == "length": + print("[Response truncated - increase max_tokens]") + elif chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") +``` + +#### Issue: Async code not working +**Solution**: Ensure you're using AsyncOpenAI and await properly: +```python +from openai import AsyncOpenAI +import asyncio + +async def main(): + client = AsyncOpenAI() + response = await client.chat.completions.create(...) + print(response.choices[0].message.content) + +asyncio.run(main()) +``` + +#### Issue: File uploads failing +**Solution**: Use proper file handling with context managers: +```python +from openai import OpenAI + +client = OpenAI() + +# Correct approach +with open("file.txt", "rb") as f: + file_obj = client.files.create(file=f, purpose="assistants") + +# Or use file_from_path helper +from openai import file_from_path +file_obj = client.files.create(file=file_from_path("file.txt"), purpose="assistants") +``` + +#### Issue: Azure OpenAI authentication errors +**Solution**: Verify you're using the correct authentication method: +```python +from openai import AzureOpenAI + +# Correct for Azure API key +client = AzureOpenAI( + api_key="your-azure-key", # NOT OpenAI key + azure_endpoint="https://your-resource.openai.azure.com/", + api_version="2024-02-15-preview" +) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/models.md b/.tessl/tiles/tessl/pypi-openai/docs/models.md new file mode 100644 index 000000000..1b202182c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/models.md @@ -0,0 +1,243 @@ +# Models + +Retrieve information about available models and manage fine-tuned models. List models, get model details, and delete custom models. + +## Capabilities + +### Retrieve Model + +Get details about a specific model. + +```python { .api } +def retrieve( + self, + model: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Model: + """ + Retrieve model information. + + Args: + model: Model ID (e.g., "gpt-4", "gpt-3.5-turbo", custom fine-tuned model). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Model: Model details including ID, owner, and capabilities. + + Raises: + NotFoundError: Model not found + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Get model details +model = client.models.retrieve("gpt-4") + +print(f"ID: {model.id}") +print(f"Owner: {model.owned_by}") +print(f"Created: {model.created}") +``` + +### List Models + +List all available models. + +```python { .api } +def list( + self, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncPage[Model]: + """ + List all available models. + + Args: + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncPage[Model]: Paginated list of models. + """ +``` + +Usage examples: + +```python +# List all models +models = client.models.list() + +for model in models.data: + print(f"{model.id} - {model.owned_by}") + +# Filter for specific models +gpt4_models = [m for m in client.models.list() if "gpt-4" in m.id] + +# Find fine-tuned models +fine_tuned = [m for m in client.models.list() if "ft:" in m.id] + +for model in fine_tuned: + print(f"Fine-tuned model: {model.id}") +``` + +### Delete Model + +Delete a fine-tuned model. + +```python { .api } +def delete( + self, + model: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ModelDeleted: + """ + Delete a fine-tuned model. Only works with custom fine-tuned models. + + Args: + model: The model ID to delete (must be a fine-tuned model you own). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ModelDeleted: Deletion confirmation. + + Raises: + NotFoundError: Model not found + BadRequestError: Cannot delete base models + """ +``` + +Usage example: + +```python +# Delete fine-tuned model +result = client.models.delete("ft:gpt-3.5-turbo:custom:abc123") + +print(f"Deleted: {result.id}") +print(f"Success: {result.deleted}") + +# Note: Cannot delete base models +try: + client.models.delete("gpt-4") # Will fail +except Exception as e: + print(f"Error: {e}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Model(BaseModel): + """Model information.""" + id: str + created: int + object: Literal["model"] + owned_by: str + +class ModelDeleted(BaseModel): + """Model deletion confirmation.""" + id: str + deleted: bool + object: Literal["model"] + +# Pagination +class SyncPage[T](BaseModel): + data: list[T] + object: str + def __iter__(self) -> Iterator[T]: ... +``` + +## Common Models + +### Chat Models +- `gpt-4o`: Most capable GPT-4 Omni model +- `gpt-4-turbo`: Fast GPT-4 with 128K context +- `gpt-4`: Original GPT-4 model +- `gpt-3.5-turbo`: Fast and cost-effective + +### Reasoning Models +- `o1`: Advanced reasoning model +- `o3`: Latest reasoning model + +### Embedding Models +- `text-embedding-3-large`: Largest embedding model +- `text-embedding-3-small`: Efficient embedding model +- `text-embedding-ada-002`: Legacy embedding model + +### Other Models +- `whisper-1`: Audio transcription/translation +- `tts-1`, `tts-1-hd`: Text-to-speech +- `dall-e-3`, `dall-e-2`: Image generation +- `text-moderation-latest`: Content moderation + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Cache model list to reduce API calls +def get_available_models(): + return [m.id for m in client.models.list()] + +# 2. Verify model availability before use +def is_model_available(model_id: str) -> bool: + try: + client.models.retrieve(model_id) + return True + except: + return False + +# 3. List your fine-tuned models +def get_my_fine_tuned_models(): + return [ + m for m in client.models.list() + if "ft:" in m.id + ] + +# 4. Clean up old fine-tuned models +old_models = get_my_fine_tuned_models() +for model in old_models: + if should_delete(model): + client.models.delete(model.id) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def list_models(): + client = AsyncOpenAI() + models = await client.models.list() + return [m.id for m in models.data] + +model_ids = asyncio.run(list_models()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/moderations.md b/.tessl/tiles/tessl/pypi-openai/docs/moderations.md new file mode 100644 index 000000000..b0a0b7334 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/moderations.md @@ -0,0 +1,356 @@ +# Moderations + +Check content against OpenAI's usage policies to detect potentially harmful content across multiple categories including hate speech, violence, sexual content, and self-harm. Supports both text and image inputs for multi-modal moderation. + +## Capabilities + +### Create Moderation + +Classify text and/or image content for policy violations. + +```python { .api } +def create( + self, + *, + input: str | list[str] | list[ModerationMultiModalInputParam], + model: str | ModerationModel | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ModerationCreateResponse: + """ + Classify text and/or image inputs against OpenAI's usage policies. + + Args: + input: Content to classify. Can be: + - Single string: "Text to moderate" + - List of strings: ["Text 1", "Text 2"] + - List of multi-modal inputs: [{"type": "text", "text": "..."}, + {"type": "image_url", "image_url": {"url": "..."}}] + Maximum 32,768 characters per text input. + + model: Moderation model to use. Options: + - "text-moderation-latest": Latest text model, automatically updated + - "text-moderation-stable": Stable text model, less frequent updates + - "omni-moderation-latest": Latest multi-modal model (supports text + images, default) + - "omni-moderation-2024-09-26": Specific omni model version + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ModerationCreateResponse: Contains flagged status and category scores + for each input. + + Raises: + BadRequestError: Input exceeds maximum length + AuthenticationError: Invalid API key + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Check single text +response = client.moderations.create( + input="I want to hurt someone" +) + +result = response.results[0] +print(f"Flagged: {result.flagged}") + +if result.flagged: + print("Violated categories:") + for category, flagged in result.categories.model_dump().items(): + if flagged: + score = getattr(result.category_scores, category) + print(f" {category}: {score:.4f}") + +# Check multiple texts +texts = [ + "Hello, how are you?", + "This is inappropriate content", + "What's the weather like today?" +] + +response = client.moderations.create(input=texts) + +for i, result in enumerate(response.results): + print(f"Text {i + 1}: {'Flagged' if result.flagged else 'Safe'}") + +# Use latest omni model +response = client.moderations.create( + model="omni-moderation-latest", + input="Check this message for violations" +) + +# Use stable model for consistent behavior +response = client.moderations.create( + model="text-moderation-stable", + input="Testing moderation" +) + +# Multi-modal moderation with text and images +response = client.moderations.create( + model="omni-moderation-latest", + input=[ + {"type": "text", "text": "Check this message"}, + { + "type": "image_url", + "image_url": {"url": "https://example.com/image.jpg"} + } + ] +) + +# Moderate image from base64 +import base64 + +with open("image.jpg", "rb") as f: + image_data = base64.b64encode(f.read()).decode() + +response = client.moderations.create( + model="omni-moderation-latest", + input=[ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"} + } + ] +) + +# Detailed category analysis +response = client.moderations.create( + input="Potentially problematic text" +) + +result = response.results[0] + +# All categories and scores +categories = result.categories +scores = result.category_scores + +print("Category Analysis:") +print(f" Hate: {scores.hate:.4f} (flagged: {categories.hate})") +print(f" Hate/Threatening: {scores.hate_threatening:.4f} (flagged: {categories.hate_threatening})") +print(f" Harassment: {scores.harassment:.4f} (flagged: {categories.harassment})") +print(f" Harassment/Threatening: {scores.harassment_threatening:.4f} (flagged: {categories.harassment_threatening})") +print(f" Self-Harm: {scores.self_harm:.4f} (flagged: {categories.self_harm})") +print(f" Self-Harm/Intent: {scores.self_harm_intent:.4f} (flagged: {categories.self_harm_intent})") +print(f" Self-Harm/Instructions: {scores.self_harm_instructions:.4f} (flagged: {categories.self_harm_instructions})") +print(f" Sexual: {scores.sexual:.4f} (flagged: {categories.sexual})") +print(f" Sexual/Minors: {scores.sexual_minors:.4f} (flagged: {categories.sexual_minors})") +print(f" Violence: {scores.violence:.4f} (flagged: {categories.violence})") +print(f" Violence/Graphic: {scores.violence_graphic:.4f} (flagged: {categories.violence_graphic})") + +# Filter user content example +def is_safe_content(text: str) -> tuple[bool, list[str]]: + """ + Check if content is safe to use. + Returns (is_safe, violated_categories) + """ + response = client.moderations.create(input=text) + result = response.results[0] + + if not result.flagged: + return True, [] + + violated = [ + category for category, flagged in result.categories.model_dump().items() + if flagged + ] + + return False, violated + +# Use in application +user_input = "Some user-generated content" +is_safe, violations = is_safe_content(user_input) + +if is_safe: + print("Content approved") +else: + print(f"Content rejected. Violations: {', '.join(violations)}") +``` + +## Types + +```python { .api } +from typing import Literal, Union +from typing_extensions import TypedDict +from pydantic import BaseModel + +class ModerationCreateResponse(BaseModel): + """Moderation response.""" + id: str + model: str + results: list[ModerationResult] + +class ModerationResult(BaseModel): + """Single moderation result.""" + flagged: bool + categories: ModerationCategories + category_scores: ModerationCategoryScores + category_applied_input_types: ModerationCategoryAppliedInputTypes + +class ModerationCategories(BaseModel): + """Category flags (true if violated).""" + hate: bool + hate_threatening: bool + harassment: bool + harassment_threatening: bool + self_harm: bool + self_harm_intent: bool + self_harm_instructions: bool + sexual: bool + sexual_minors: bool + violence: bool + violence_graphic: bool + illicit: bool + illicit_violent: bool + +class ModerationCategoryScores(BaseModel): + """Confidence scores (0-1) for each category.""" + hate: float + hate_threatening: float + harassment: float + harassment_threatening: float + self_harm: float + self_harm_intent: float + self_harm_instructions: float + sexual: float + sexual_minors: float + violence: float + violence_graphic: float + illicit: float + illicit_violent: float + +class ModerationCategoryAppliedInputTypes(BaseModel): + """Input types that triggered each category.""" + hate: list[str] + hate_threatening: list[str] + harassment: list[str] + harassment_threatening: list[str] + self_harm: list[str] + self_harm_intent: list[str] + self_harm_instructions: list[str] + sexual: list[str] + sexual_minors: list[str] + violence: list[str] + violence_graphic: list[str] + illicit: list[str] + illicit_violent: list[str] + +# Model type +ModerationModel = Literal[ + "text-moderation-latest", + "text-moderation-stable", + "omni-moderation-latest", + "omni-moderation-2024-09-26" +] + +# Multi-modal input types +class ModerationTextInputParam(TypedDict): + """Text input for moderation.""" + text: str # Required: Text content to moderate + type: Literal["text"] # Required: Always "text" + +class ImageURL(TypedDict): + """Image URL or base64 data.""" + url: str # Required: URL or data:image/...;base64,... string + +class ModerationImageURLInputParam(TypedDict): + """Image input for moderation.""" + image_url: ImageURL # Required: Image URL or base64 data + type: Literal["image_url"] # Required: Always "image_url" + +# Union type for multi-modal inputs +ModerationMultiModalInputParam = Union[ + ModerationTextInputParam, + ModerationImageURLInputParam +] +``` + +## Category Descriptions + +| Category | Description | +|----------|-------------| +| hate | Content expressing, inciting, or promoting hate based on protected characteristics | +| hate/threatening | Hateful content that also includes violence or serious harm | +| harassment | Content harassing, bullying, or abusing an individual | +| harassment/threatening | Harassing content that also includes violence or serious harm | +| self-harm | Content promoting, encouraging, or depicting acts of self-harm | +| self-harm/intent | Content indicating intent to engage in self-harm | +| self-harm/instructions | Content providing instructions or advice for self-harm | +| sexual | Content meant to arouse sexual excitement | +| sexual/minors | Sexual content involving individuals under 18 | +| violence | Content depicting death, violence, or physical injury | +| violence/graphic | Graphic violent content with extreme detail | +| illicit | Content promoting illicit substances or illegal activities | +| illicit/violent | Illicit content involving violence | + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Moderate user-generated content before processing +def moderate_before_processing(user_text: str): + response = client.moderations.create(input=user_text) + + if response.results[0].flagged: + return None, "Content violates policies" + + # Safe to process + return process_safe_content(user_text), None + +# 2. Batch moderation for efficiency +user_messages = ["msg1", "msg2", "msg3"] +response = client.moderations.create(input=user_messages) + +safe_messages = [ + msg for msg, result in zip(user_messages, response.results) + if not result.flagged +] + +# 3. Log violations for analysis +for i, result in enumerate(response.results): + if result.flagged: + violated_categories = [ + cat for cat, flagged in result.categories.model_dump().items() + if flagged + ] + log_violation(user_messages[i], violated_categories) + +# 4. Use thresholds for borderline content +def is_definitely_safe(text: str, threshold: float = 0.5) -> bool: + response = client.moderations.create(input=text) + result = response.results[0] + + # Check if any score exceeds threshold + scores = result.category_scores.model_dump() + return all(score < threshold for score in scores.values()) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def moderate_content(text: str): + client = AsyncOpenAI() + + response = await client.moderations.create(input=text) + return response.results[0].flagged + +# Run async +is_flagged = asyncio.run(moderate_content("Check this text")) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/realtime.md b/.tessl/tiles/tessl/pypi-openai/docs/realtime.md new file mode 100644 index 000000000..3882b6129 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/realtime.md @@ -0,0 +1,588 @@ +# Realtime API + +WebSocket-based realtime communication for low-latency conversational AI experiences with audio streaming, function calling, and interruption handling. + +## Capabilities + +### Create Realtime Session + +Establish a WebSocket connection for realtime interaction. + +```python { .api } +def create( + self, + *, + model: str, + modalities: list[str] | Omit = omit, + instructions: str | Omit = omit, + voice: str | Omit = omit, + input_audio_format: str | Omit = omit, + output_audio_format: str | Omit = omit, + input_audio_transcription: dict | Omit = omit, + turn_detection: dict | Omit = omit, + tools: list[dict] | Omit = omit, + tool_choice: str | Omit = omit, + temperature: float | Omit = omit, + max_response_output_tokens: int | str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RealtimeSession: + """ + Create a realtime session for WebSocket communication. + + Args: + model: Model to use (e.g., "gpt-4o-realtime-preview"). + + modalities: Response modalities. Options: ["text", "audio"]. + + instructions: System instructions for the session. + + voice: Voice for audio output. Options: "alloy", "echo", "fable", + "onyx", "nova", "shimmer". + + input_audio_format: Input audio format. Options: "pcm16", "g711_ulaw", + "g711_alaw". + + output_audio_format: Output audio format. Options: "pcm16", "g711_ulaw", + "g711_alaw", "mp3", "opus". + + input_audio_transcription: Enable input transcription. + {"model": "whisper-1"} + + turn_detection: Turn detection configuration. + {"type": "server_vad", "threshold": 0.5, "prefix_padding_ms": 300, + "silence_duration_ms": 200} + + tools: Function tools available to the model. + + tool_choice: Tool choice configuration. "auto", "none", "required". + + temperature: Sampling temperature. + + max_response_output_tokens: Maximum output tokens per response. + + Returns: + RealtimeSession: WebSocket session configuration. + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create realtime session +session = client.beta.realtime.sessions.create( + model="gpt-4o-realtime-preview", + modalities=["text", "audio"], + voice="alloy", + instructions="You are a helpful assistant.", + input_audio_format="pcm16", + output_audio_format="pcm16", + turn_detection={ + "type": "server_vad", + "threshold": 0.5, + "silence_duration_ms": 500 + } +) + +# Access WebSocket URL +ws_url = session.client_secret.value + +# Use with WebSocket library (e.g., websockets) +import asyncio +import websockets +import json + +async def realtime_conversation(): + async with websockets.connect(ws_url) as websocket: + # Send audio input + await websocket.send(json.dumps({ + "type": "input_audio_buffer.append", + "audio": base64_audio_data + })) + + # Commit audio + await websocket.send(json.dumps({ + "type": "input_audio_buffer.commit" + })) + + # Receive responses + async for message in websocket: + event = json.loads(message) + + if event["type"] == "response.audio.delta": + # Handle audio chunk + audio_chunk = event["delta"] + # Play or process audio + + elif event["type"] == "response.text.delta": + # Handle text chunk + text = event["delta"] + print(text, end="", flush=True) + + elif event["type"] == "response.done": + break + +asyncio.run(realtime_conversation()) +``` + +### Realtime Calls + +Manage incoming and outgoing realtime voice calls with call control methods. + +```python { .api } +# Accessed via: client.realtime.calls or client.beta.realtime.calls +def create( + self, + *, + model: str, + modalities: list[str] | Omit = omit, + instructions: str | Omit = omit, + voice: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RealtimeCall: + """ + Initiate an outgoing realtime call. + + Args: + model: Model to use for the call. + modalities: Response modalities (["audio"] recommended). + instructions: System instructions for the call. + voice: Voice for audio output. + + Returns: + RealtimeCall: Call object with connection details. + """ + +def accept( + self, + call_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RealtimeCall: + """ + Accept an incoming realtime call. + + Args: + call_id: The ID of the incoming call to accept. + + Returns: + RealtimeCall: Call object with status "active". + """ + +def hangup( + self, + call_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RealtimeCall: + """ + End an active realtime call. + + Args: + call_id: The ID of the call to hang up. + + Returns: + RealtimeCall: Call object with status "completed". + """ + +def refer( + self, + call_id: str, + *, + refer_to: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RealtimeCall: + """ + Transfer a realtime call to another destination. + + Args: + call_id: The ID of the call to transfer. + refer_to: Destination identifier to transfer the call to. + + Returns: + RealtimeCall: Call object with status "referred". + """ + +def reject( + self, + call_id: str, + *, + reason: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RealtimeCall: + """ + Reject an incoming realtime call. + + Args: + call_id: The ID of the incoming call to reject. + reason: Optional reason for rejection. + + Returns: + RealtimeCall: Call object with status "rejected". + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create an outgoing call (available at both paths) +call = client.realtime.calls.create( + model="gpt-4o-realtime-preview", + modalities=["audio"], + voice="alloy", + instructions="You are a helpful phone assistant." +) + +print(f"Call ID: {call.id}, Status: {call.status}") + +# Accept an incoming call +accepted_call = client.realtime.calls.accept("call_abc123") + +# Transfer a call +referred_call = client.realtime.calls.refer( + call.id, + refer_to="destination_id" +) + +# End a call +ended_call = client.realtime.calls.hangup(call.id) +print(f"Call ended: {ended_call.status}") + +# Reject an incoming call +rejected_call = client.realtime.calls.reject( + "call_xyz789", + reason="User unavailable" +) +``` + +### Client Secrets + +Create ephemeral client secrets for secure realtime session establishment. + +```python { .api } +# Access via client.realtime.client_secrets + +def create( + self, + *, + expires_after: dict | Omit = omit, + session: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ClientSecretCreateResponse: + """ + Create a Realtime client secret with an associated session configuration. + + Args: + expires_after: Configuration for the client secret expiration. + Expiration refers to the time after which a client secret will + no longer be valid for creating sessions. The session itself may + continue after that time once started. A secret can be used to + create multiple sessions until it expires. + Example: {"anchor": "created_at", "seconds": 3600} + + session: Session configuration to use for the client secret. + Choose either a realtime session or a transcription session. + Example for realtime: { + "type": "realtime", + "model": "gpt-4o-realtime-preview", + "voice": "alloy", + "modalities": ["text", "audio"] + } + Example for transcription: { + "type": "transcription", + "model": "whisper-1" + } + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + ClientSecretCreateResponse: Created client secret with value and + expiration time. Use the secret value to establish WebSocket + connections from client-side applications. + + Notes: + - Client secrets enable secure browser-based realtime connections + - Secrets expire after specified duration + - One secret can establish multiple sessions until expiration + - Use for temporary, client-side authentication + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create client secret for realtime session +secret = client.realtime.client_secrets.create( + expires_after={ + "anchor": "created_at", + "seconds": 3600 # Expires in 1 hour + }, + session={ + "type": "realtime", + "model": "gpt-4o-realtime-preview", + "voice": "alloy", + "modalities": ["text", "audio"], + "instructions": "You are a helpful voice assistant." + } +) + +print(f"Client Secret: {secret.value}") +print(f"Expires At: {secret.expires_at}") + +# Create client secret for transcription session +transcription_secret = client.realtime.client_secrets.create( + expires_after={ + "anchor": "created_at", + "seconds": 1800 # Expires in 30 minutes + }, + session={ + "type": "transcription", + "model": "whisper-1", + "input_audio_format": "pcm16", + "input_audio_transcription": { + "model": "whisper-1" + } + } +) + +# Use the secret client-side for WebSocket connection +# The secret value is passed as authentication to establish the connection +``` + +### Transcription Sessions + +Create ephemeral API tokens for client-side realtime transcription applications. + +```python { .api } +def create( + self, + *, + client_secret: dict | Omit = omit, + include: list[str] | Omit = omit, + input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | Omit = omit, + input_audio_noise_reduction: dict | Omit = omit, + input_audio_transcription: dict | Omit = omit, + modalities: list[Literal["text", "audio"]] | Omit = omit, + turn_detection: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> TranscriptionSession: + """ + Create an ephemeral API token for client-side realtime transcriptions. + + Returns a session object with a client_secret containing an ephemeral + API token for authenticating browser clients. + + Args: + client_secret: Configuration options for the generated client secret. + + include: Items to include in the transcription. Options: + - "item.input_audio_transcription.logprobs" + + input_audio_format: Input audio format. Options: "pcm16", "g711_ulaw", + "g711_alaw". For pcm16, audio must be 16-bit PCM at 24kHz sample rate, + single channel (mono), little-endian byte order. + + input_audio_noise_reduction: Configuration for input audio noise reduction. + Filters audio added to the input buffer before VAD and model processing. + Can improve VAD accuracy and model performance. + + input_audio_transcription: Configuration for input audio transcription. + Can optionally set language and prompt for additional guidance. + + modalities: Response modalities. Options: ["text"], ["audio"], or both. + To disable audio, set to ["text"]. + + turn_detection: Configuration for turn detection (Server VAD or Semantic VAD). + Set to null to turn off, requiring manual trigger of model response. + Server VAD detects speech based on audio volume. Semantic VAD uses + a turn detection model to estimate turn completion and dynamically + sets timeout based on probability. + + Returns: + TranscriptionSession: Session with client_secret for browser authentication. + """ +``` + +**Usage Example:** + +```python +from openai import OpenAI + +client = OpenAI() + +# Create transcription session for client-side use +session = client.beta.realtime.transcription_sessions.create( + input_audio_format="pcm16", + input_audio_transcription={ + "model": "whisper-1", + "language": "en", + "prompt": "Technical discussion" + }, + input_audio_noise_reduction={ + "type": "default" + }, + turn_detection={ + "type": "semantic_vad", + "threshold": 0.6 + }, + modalities=["text", "audio"], + include=["item.input_audio_transcription.logprobs"] +) + +# Use the client_secret in browser/client application +ephemeral_token = session.client_secret.value +print(f"Session ID: {session.id}") +print(f"Token expires: {session.client_secret.expires_at}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class RealtimeSession(BaseModel): + """Realtime session configuration.""" + id: str + model: str + modalities: list[str] + instructions: str | None + voice: str | None + input_audio_format: str + output_audio_format: str + input_audio_transcription: dict | None + turn_detection: dict | None + tools: list[dict] | None + tool_choice: str + temperature: float | None + max_response_output_tokens: int | str | None + client_secret: ClientSecret + +class ClientSecret(BaseModel): + """WebSocket client secret.""" + value: str + expires_at: int + +class ClientSecretCreateResponse(BaseModel): + """Response from creating a client secret.""" + id: str + created_at: int + expires_at: int + value: str # The ephemeral client secret value + session: dict # Session configuration associated with this secret +``` + +## Event Types + +WebSocket events for realtime communication: + +- `session.created` - Session established +- `input_audio_buffer.append` - Add audio data +- `input_audio_buffer.commit` - Process buffered audio +- `input_audio_buffer.clear` - Clear buffer +- `conversation.item.create` - Add conversation item +- `response.create` - Request response +- `response.cancel` - Cancel current response +- `response.audio.delta` - Audio chunk received +- `response.text.delta` - Text chunk received +- `response.done` - Response completed +- `conversation.item.input_audio_transcription.completed` - Transcription ready +- `error` - Error occurred + +## Best Practices + +```python +import asyncio +import websockets +import json +import base64 + +async def realtime_session(session_url: str): + async with websockets.connect(session_url) as ws: + # Handle incoming events + async def receive_events(): + async for message in ws: + event = json.loads(message) + + if event["type"] == "response.audio.delta": + # Stream audio to speaker + audio_data = base64.b64decode(event["delta"]) + play_audio(audio_data) + + elif event["type"] == "response.text.delta": + # Display text + print(event["delta"], end="", flush=True) + + # Send audio input + async def send_audio(): + while True: + audio_chunk = record_audio_chunk() + await ws.send(json.dumps({ + "type": "input_audio_buffer.append", + "audio": base64.b64encode(audio_chunk).decode() + })) + await asyncio.sleep(0.1) + + # Run both tasks + await asyncio.gather( + receive_events(), + send_audio() + ) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_session(): + client = AsyncOpenAI() + + session = await client.beta.realtime.sessions.create( + model="gpt-4o-realtime-preview", + modalities=["audio"] + ) + + return session.client_secret.value + +ws_url = asyncio.run(create_session()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/responses.md b/.tessl/tiles/tessl/pypi-openai/docs/responses.md new file mode 100644 index 000000000..05867f490 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/responses.md @@ -0,0 +1,581 @@ +# Responses API + +Create responses with advanced tool support including computer use, file search, and code patching. The Responses API provides an enhanced interface for complex assistant interactions. + +## Capabilities + +### Create Response + +Generate a response with advanced tools and capabilities. + +```python { .api } +def create( + self, + *, + model: str, + input: dict | list[dict], + instructions: str | Omit = omit, + metadata: dict[str, str] | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + reasoning_effort: str | Omit = omit, + store: bool | Omit = omit, + stream: bool | Omit = omit, + temperature: float | Omit = omit, + tool_choice: str | dict | Omit = omit, + tools: list[dict] | Omit = omit, + top_p: float | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Response: + """ + Create a response with advanced tools. + + Args: + model: Model ID to use. + + input: Input message(s). Can be: + - Single message: {"role": "user", "content": "..."} + - List of messages: [{"role": "user", "content": "..."}, ...] + + instructions: System instructions for the response. + + metadata: Key-value pairs (max 16). + + parallel_tool_calls: Enable parallel tool execution. + + reasoning_effort: Reasoning level for reasoning models. + + store: If true, store response for later retrieval. + + stream: Enable streaming response. + + temperature: Sampling temperature. + + tool_choice: Tool choice configuration. + + tools: Available tools. Options: + - {"type": "function", "function": {...}}: Custom functions + - {"type": "file_search"}: File search + - {"type": "computer_use"}: Computer use (screen/keyboard/mouse) + - {"type": "apply_patch"}: Code patching + + top_p: Nucleus sampling. + + Returns: + Response: Generated response with tool results. + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic response +response = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Hello!"} +) + +print(response.output[0].content) + +# With file search +response = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Find information about Python."}, + tools=[{"type": "file_search"}] +) + +# With computer use (beta) +response = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Click the button."}, + tools=[{ + "type": "computer_use", + "display_width_px": 1920, + "display_height_px": 1080 + }] +) + +# With code patching +response = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Fix the bug in this code."}, + tools=[{"type": "apply_patch"}] +) + +# Multi-turn conversation +messages = [ + {"role": "user", "content": "What is 2+2?"}, + {"role": "assistant", "content": "4"}, + {"role": "user", "content": "What about 3+3?"} +] + +response = client.responses.create( + model="gpt-4", + input=messages +) + +# Streaming response +stream = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Tell me a story."}, + stream=True +) + +for chunk in stream: + if chunk.delta: + print(chunk.delta.content, end="", flush=True) +``` + +### Retrieve Response + +Get a stored response by ID. + +```python { .api } +def retrieve( + self, + response_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Response: + """Retrieve a stored response.""" +``` + +### Delete Response + +Delete a stored response by ID. + +```python { .api } +def delete( + self, + response_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> None: + """ + Delete a response with the given ID. + + Args: + response_id: The ID of the response to delete. + """ +``` + +### Cancel Response + +Cancel an in-progress response. + +```python { .api } +def cancel( + self, + response_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Response: + """ + Cancel an in-progress response. + + Only responses created with the `background` parameter set to `true` can be cancelled. + + Args: + response_id: The ID of the response to cancel. + """ +``` + +### Stream Response + +Stream a response in real-time, either creating a new response or streaming an existing one. + +```python { .api } +def stream( + self, + *, + response_id: str = omit, + input: str | dict | list[dict] | None = omit, + model: str | None = omit, + background: bool | None = omit, + text_format: type = omit, + tools: list[dict] | None = omit, + conversation: dict | None = omit, + include: list[str] | None = omit, + instructions: str | None = omit, + max_output_tokens: int | None = omit, + max_tool_calls: int | None = omit, + metadata: dict[str, str] | None = omit, + parallel_tool_calls: bool | None = omit, + previous_response_id: str | None = omit, + prompt: dict | None = omit, + prompt_cache_key: str | None = omit, + prompt_cache_retention: Literal["in-memory", "24h"] | None = omit, + reasoning: dict | None = omit, + safety_identifier: str | None = omit, + service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None = omit, + store: bool | None = omit, + stream_options: dict | None = omit, + temperature: float | None = omit, + text: dict | None = omit, + tool_choice: str | dict | None = omit, + top_logprobs: int | None = omit, + top_p: float | None = omit, + truncation: Literal["auto", "disabled"] | None = omit, + user: str | None = omit, + starting_after: int | None = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ResponseStreamManager: + """ + Stream a response. Can either create a new response or stream an existing one. + + Args: + response_id: If provided, stream an existing response. Otherwise, create a new response. + input: Input message(s) for creating a new response. + model: Model ID to use for creating a new response. + text_format: Parse streaming text into a specific format type. + starting_after: For streaming existing responses, start streaming after this item index. + + Returns: + ResponseStreamManager: Streaming response manager for iterating over chunks. + """ +``` + +### Parse Response + +Create a response with structured output parsing using a format type. + +```python { .api } +def parse( + self, + *, + text_format: type, + background: bool | None = omit, + conversation: dict | None = omit, + include: list[str] | None = omit, + input: str | dict | list[dict] | None = omit, + instructions: str | None = omit, + max_output_tokens: int | None = omit, + max_tool_calls: int | None = omit, + metadata: dict[str, str] | None = omit, + model: str | None = omit, + parallel_tool_calls: bool | None = omit, + previous_response_id: str | None = omit, + prompt: dict | None = omit, + prompt_cache_key: str | None = omit, + prompt_cache_retention: Literal["in-memory", "24h"] | None = omit, + reasoning: dict | None = omit, + safety_identifier: str | None = omit, + service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None = omit, + store: bool | None = omit, + stream: Literal[False] | Literal[True] | None = omit, + stream_options: dict | None = omit, + temperature: float | None = omit, + text: dict | None = omit, + tool_choice: str | dict | None = omit, + tools: list[dict] | None = omit, + top_logprobs: int | None = omit, + top_p: float | None = omit, + truncation: Literal["auto", "disabled"] | None = omit, + user: str | None = omit, + verbosity: Literal["low", "medium", "high"] | None = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ParsedResponse: + """ + Create a response with structured output parsing. + + Args: + text_format: A type (Pydantic model or similar) to parse the response into. + input: Input message(s). + model: Model ID to use. + + Returns: + ParsedResponse: Response with parsed structured output. + """ +``` + +### Input Items + +List input items for a response. + +```python { .api } +def input_items.list( + self, + response_id: str, + *, + after: str | None = omit, + include: list[str] | None = omit, + limit: int | None = omit, + order: Literal["asc", "desc"] | None = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[ResponseItem]: + """ + Returns a list of input items for a given response. + + Args: + response_id: The ID of the response. + after: An item ID to list items after, used in pagination. + include: Additional fields to include in the response. + limit: Limit on number of objects (1-100, default 20). + order: Order to return items ('asc' or 'desc', default 'desc'). + + Returns: + Paginated list of response input items. + """ +``` + +### Input Tokens + +Count input tokens for a potential response. + +```python { .api } +def input_tokens.count( + self, + *, + conversation: dict | None = omit, + input: str | list[dict] | None = omit, + instructions: str | None = omit, + model: str | None = omit, + parallel_tool_calls: bool | None = omit, + previous_response_id: str | None = omit, + reasoning: dict | None = omit, + text: dict | None = omit, + tool_choice: dict | None = omit, + tools: list[dict] | None = omit, + truncation: Literal["auto", "disabled"] | None = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> InputTokenCountResponse: + """ + Get input token counts for a potential response without making the request. + + Args: + conversation: The conversation context. + input: Text, image, or file inputs. + instructions: System instructions. + model: Model ID. + tools: Available tools. + + Returns: + InputTokenCountResponse: Token count information. + """ +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Response(BaseModel): + """Response object.""" + id: str + created_at: int + model: str + object: Literal["response"] + output: list[ResponseContent] + status: Literal["in_progress", "completed", "failed", "cancelled"] + tool_calls: list[ToolCall] | None + usage: Usage | None + +class ResponseContent(BaseModel): + """Response content.""" + role: Literal["assistant"] + content: str + type: Literal["text"] + +class ToolCall(BaseModel): + """Tool invocation.""" + id: str + type: str + function: dict | None + computer_use: dict | None + apply_patch: dict | None + +class Usage(BaseModel): + """Token usage.""" + input_tokens: int + output_tokens: int + total_tokens: int +``` + +## Tool Types + +### Computer Use + +```python +{ + "type": "computer_use", + "display_width_px": 1920, + "display_height_px": 1080, + "display_number": 1 # Optional, for multi-monitor +} +``` + +Enables the model to interact with a computer: +- View screenshots +- Control mouse (move, click) +- Type on keyboard +- Execute commands + +### Apply Patch + +```python +{ + "type": "apply_patch" +} +``` + +Enables code modification: +- Generate code patches +- Apply changes to files +- Fix bugs +- Refactor code + +### File Search + +```python +{ + "type": "file_search" +} +``` + +Search uploaded files and documents. + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Use for complex multi-step tasks +response = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Analyze this codebase and fix all bugs."}, + tools=[ + {"type": "file_search"}, + {"type": "apply_patch"} + ] +) + +# 2. Handle tool calls +if response.tool_calls: + for tool_call in response.tool_calls: + if tool_call.type == "apply_patch": + # Review and apply patch + patch = tool_call.apply_patch + print(f"Patch: {patch}") + +# 3. Store important responses +response = client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Important analysis"}, + store=True +) + +# Retrieve later +stored = client.responses.retrieve(response.id) +``` + +### Compact Conversation + +Compact a conversation to reduce its size while maintaining context and essential information. This optimization utility helps manage token usage and costs for long conversations. + +```python { .api } +def compact( + self, + *, + model: str, + input: str | list[dict] | Omit = omit, + instructions: str | Omit = omit, + previous_response_id: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> CompactedResponse: + """ + Compact conversation to optimize token usage. + + This method compresses conversation history while preserving essential + context, enabling more efficient multi-turn conversations by reducing + token consumption. + + Args: + model: Model ID used to generate the response. OpenAI offers a + wide range of models with different capabilities and price points. + + input: Text, image, or file inputs to the model. Can be a string + or list of input item dictionaries. + + instructions: System (developer) message for the model's context. + When used with previous_response_id, previous instructions + are not carried over. + + previous_response_id: Unique ID of the previous response to create + multi-turn conversations. Cannot be used with conversation parameter. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + CompactedResponse: Compacted response with reduced token count. + """ +``` + +Usage example: + +```python +# Compact a long conversation +compacted = client.responses.compact( + model="gpt-4", + input="Summarize the key points from our previous discussion", + previous_response_id="resp-abc123" +) + +print(f"Original tokens: {compacted.original_token_count}") +print(f"Compacted tokens: {compacted.token_count}") +print(f"Reduction: {compacted.reduction_percentage}%") +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_response(): + client = AsyncOpenAI() + + response = await client.responses.create( + model="gpt-4", + input={"role": "user", "content": "Hello!"} + ) + + return response.output[0].content + +content = asyncio.run(create_response()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/runs.md b/.tessl/tiles/tessl/pypi-openai/docs/runs.md new file mode 100644 index 000000000..0bea9f294 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/runs.md @@ -0,0 +1,725 @@ +# Runs + +Execute assistants on threads and handle tool calls. Runs represent the execution of an assistant on a thread, managing the conversation flow and tool interactions. + +## Capabilities + +### Create Run + +Execute an assistant on a thread. + +```python { .api } +def create( + self, + thread_id: str, + *, + assistant_id: str, + additional_instructions: str | Omit = omit, + additional_messages: list[dict] | Omit = omit, + instructions: str | Omit = omit, + max_completion_tokens: int | Omit = omit, + max_prompt_tokens: int | Omit = omit, + metadata: dict[str, str] | Omit = omit, + model: str | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: dict | Omit = omit, + stream: bool | Omit = omit, + temperature: float | Omit = omit, + tool_choice: str | dict | Omit = omit, + tools: list[dict] | Omit = omit, + top_p: float | Omit = omit, + truncation_strategy: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """ + Run an assistant on a thread. + + Args: + thread_id: The thread ID. + assistant_id: The assistant ID. + additional_instructions: Append to assistant instructions. + additional_messages: Add messages before run. + instructions: Override assistant instructions. + max_completion_tokens: Maximum completion tokens. + max_prompt_tokens: Maximum prompt tokens. + metadata: Key-value pairs (max 16). + model: Override assistant model. + parallel_tool_calls: Enable parallel tool calls. + response_format: Override response format. + stream: Enable streaming. + temperature: Sampling temperature. + tool_choice: Tool choice configuration. + tools: Override assistant tools. + top_p: Nucleus sampling. + truncation_strategy: Message truncation config. + + Returns: + Run: Created run. + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Basic run +run = client.beta.threads.runs.create( + thread_id="thread_abc123", + assistant_id="asst_abc123" +) + +print(f"Run ID: {run.id}") +print(f"Status: {run.status}") + +# With additional instructions +run = client.beta.threads.runs.create( + thread_id="thread_abc123", + assistant_id="asst_abc123", + additional_instructions="Be concise." +) + +# Override model +run = client.beta.threads.runs.create( + thread_id="thread_abc123", + assistant_id="asst_abc123", + model="gpt-4-turbo" +) + +# With streaming +stream = client.beta.threads.runs.create( + thread_id="thread_abc123", + assistant_id="asst_abc123", + stream=True +) + +for event in stream: + print(event) +``` + +### Retrieve Run + +Get run status and details. + +```python { .api } +def retrieve( + self, + thread_id: str, + run_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """Get run details.""" +``` + +Usage example: + +```python +run = client.beta.threads.runs.retrieve( + thread_id="thread_abc123", + run_id="run_abc123" +) + +print(f"Status: {run.status}") +print(f"Model: {run.model}") +``` + +### Update Run + +Update a run's metadata. + +```python { .api } +def update( + self, + run_id: str, + *, + thread_id: str, + metadata: dict[str, str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """ + Modify a run's metadata. + + Args: + run_id: The run ID to update. + thread_id: The thread ID containing the run. + metadata: Set of 16 key-value pairs for storing additional information. + Keys max 64 characters, values max 512 characters. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Run: Updated run object. + """ +``` + +Usage example: + +```python +# Update run metadata +run = client.beta.threads.runs.update( + run_id="run_abc123", + thread_id="thread_abc123", + metadata={ + "user_id": "user-456", + "priority": "high" + } +) + +print(f"Updated metadata: {run.metadata}") +``` + +### List Runs + +List runs for a thread. + +```python { .api } +def list( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Run]: + """List runs for a thread.""" +``` + +### Cancel Run + +Cancel an in-progress run. + +```python { .api } +def cancel( + self, + thread_id: str, + run_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """Cancel a run.""" +``` + +Usage example: + +```python +run = client.beta.threads.runs.cancel( + thread_id="thread_abc123", + run_id="run_abc123" +) + +print(f"Status: {run.status}") # "cancelling" or "cancelled" +``` + +### Submit Tool Outputs + +Submit results of tool calls back to the run. + +```python { .api } +def submit_tool_outputs( + self, + thread_id: str, + run_id: str, + *, + tool_outputs: list[dict], + stream: bool | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """ + Submit tool call results. + + Args: + thread_id: The thread ID. + run_id: The run ID. + tool_outputs: List of tool outputs. + [{"tool_call_id": "call_abc", "output": "result"}] + stream: Enable streaming. + + Returns: + Run: Updated run. + """ +``` + +Usage example: + +```python +# Check for required actions +run = client.beta.threads.runs.retrieve( + thread_id="thread_abc123", + run_id="run_abc123" +) + +if run.status == "requires_action": + tool_calls = run.required_action.submit_tool_outputs.tool_calls + + # Execute tool calls + tool_outputs = [] + for tool_call in tool_calls: + function_name = tool_call.function.name + arguments = tool_call.function.arguments + + # Call your function + result = execute_function(function_name, arguments) + + tool_outputs.append({ + "tool_call_id": tool_call.id, + "output": str(result) + }) + + # Submit outputs + run = client.beta.threads.runs.submit_tool_outputs( + thread_id="thread_abc123", + run_id="run_abc123", + tool_outputs=tool_outputs + ) +``` + +### Polling Helpers + +Wait for run completion with automatic polling. + +```python { .api } +def create_and_poll( + self, + thread_id: str, + *, + assistant_id: str, + poll_interval_ms: int = 1000, + **kwargs +) -> Run: + """Create run and poll until completion.""" + +def poll( + self, + thread_id: str, + run_id: str, + *, + poll_interval_ms: int = 1000, +) -> Run: + """Poll run until completion.""" + +def submit_tool_outputs_and_poll( + self, + thread_id: str, + run_id: str, + *, + tool_outputs: list[dict], + poll_interval_ms: int = 1000, +) -> Run: + """Submit tool outputs and poll until completion.""" +``` + +Usage examples: + +```python +# Create and wait for completion +run = client.beta.threads.runs.create_and_poll( + thread_id="thread_abc123", + assistant_id="asst_abc123" +) + +print(f"Final status: {run.status}") + +# Poll existing run +run = client.beta.threads.runs.poll( + thread_id="thread_abc123", + run_id="run_abc123" +) + +# Submit and wait +run = client.beta.threads.runs.submit_tool_outputs_and_poll( + thread_id="thread_abc123", + run_id="run_abc123", + tool_outputs=[{"tool_call_id": "call_abc", "output": "42"}] +) +``` + +### Streaming + +Stream run events in real-time. + +```python { .api } +def stream( + self, + thread_id: str, + *, + assistant_id: str, + **kwargs +) -> AssistantStreamManager: + """Stream run events with event handler.""" + +def create_and_stream( + self, + thread_id: str, + *, + assistant_id: str, + **kwargs +) -> AssistantStreamManager: + """Create run and stream events.""" +``` + +Usage example: + +```python +from openai import AssistantEventHandler + +# Define event handler +class EventHandler(AssistantEventHandler): + def on_text_created(self, text): + print(f"\\nassistant > ", end="", flush=True) + + def on_text_delta(self, delta, snapshot): + print(delta.value, end="", flush=True) + + def on_tool_call_created(self, tool_call): + print(f"\\nassistant > {tool_call.type}\\n", flush=True) + +# Stream events +with client.beta.threads.runs.stream( + thread_id="thread_abc123", + assistant_id="asst_abc123", + event_handler=EventHandler() +) as stream: + stream.until_done() +``` + +### Submit Tool Outputs with Streaming + +Submit tool outputs and stream the run to completion in real-time. + +```python { .api } +def submit_tool_outputs_stream( + self, + *, + tool_outputs: list[dict], + run_id: str, + thread_id: str, + event_handler: AssistantEventHandler | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> AssistantStreamManager[AssistantEventHandler]: + """ + Submit tool outputs and stream the run to terminal state. + + Helper method that submits tool call results and streams run events in real-time + until the run reaches a terminal state. More information on Run lifecycles: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + + Args: + tool_outputs: List of tool call outputs. + [{"tool_call_id": "call_abc", "output": "result"}] + run_id: The run ID requiring tool outputs. + thread_id: The thread ID containing the run. + event_handler: Optional custom event handler for processing stream events. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + AssistantStreamManager: Stream manager for handling assistant events. + """ +``` + +Usage example: + +```python +from openai import AssistantEventHandler + +# Stream tool output submission +with client.beta.threads.runs.submit_tool_outputs_stream( + thread_id="thread_abc123", + run_id="run_abc123", + tool_outputs=[ + {"tool_call_id": "call_abc", "output": "42"}, + {"tool_call_id": "call_def", "output": "Hello"} + ] +) as stream: + for event in stream: + if event.event == 'thread.run.completed': + print("Run completed!") + +# With custom event handler +class ToolOutputHandler(AssistantEventHandler): + def on_text_delta(self, delta, snapshot): + print(delta.value, end='', flush=True) + + def on_run_step_done(self, run_step): + print(f"\nStep {run_step.id} completed") + +with client.beta.threads.runs.submit_tool_outputs_stream( + thread_id="thread_abc123", + run_id="run_abc123", + tool_outputs=[{"tool_call_id": "call_abc", "output": "result"}], + event_handler=ToolOutputHandler() +) as stream: + stream.until_done() +``` + +### Run Steps + +View detailed steps of a run. + +```python { .api } +def retrieve( + self, + thread_id: str, + run_id: str, + step_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> RunStep: + """Retrieve run step details.""" + +def list( + self, + thread_id: str, + run_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[RunStep]: + """List run steps.""" +``` + +Usage example: + +```python +# List steps +steps = client.beta.threads.runs.steps.list( + thread_id="thread_abc123", + run_id="run_abc123" +) + +for step in steps: + print(f"Step: {step.type}") + print(f"Status: {step.status}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Run(BaseModel): + """Assistant run.""" + id: str + assistant_id: str + cancelled_at: int | None + completed_at: int | None + created_at: int + expires_at: int + failed_at: int | None + incomplete_details: dict | None + instructions: str + last_error: dict | None + max_completion_tokens: int | None + max_prompt_tokens: int | None + metadata: dict[str, str] | None + model: str + object: Literal["thread.run"] + parallel_tool_calls: bool + required_action: RequiredAction | None + response_format: dict | None + started_at: int | None + status: Literal[ + "queued", "in_progress", "requires_action", + "cancelling", "cancelled", "failed", + "completed", "incomplete", "expired" + ] + thread_id: str + tool_choice: dict | str + tools: list[dict] + truncation_strategy: dict | None + usage: Usage | None + temperature: float | None + top_p: float | None + +class RequiredAction(BaseModel): + """Required action for run.""" + type: Literal["submit_tool_outputs"] + submit_tool_outputs: SubmitToolOutputs + +class SubmitToolOutputs(BaseModel): + """Tool outputs to submit.""" + tool_calls: list[ToolCall] + +class ToolCall(BaseModel): + """Tool call from assistant.""" + id: str + type: Literal["function"] + function: Function + +class Function(BaseModel): + """Function call details.""" + name: str + arguments: str # JSON string + +class Usage(BaseModel): + """Token usage.""" + completion_tokens: int + prompt_tokens: int + total_tokens: int + +class RunStep(BaseModel): + """Individual run step.""" + id: str + assistant_id: str + cancelled_at: int | None + completed_at: int | None + created_at: int + expired_at: int | None + failed_at: int | None + last_error: dict | None + metadata: dict[str, str] | None + object: Literal["thread.run.step"] + run_id: str + status: Literal[ + "in_progress", "cancelled", "failed", + "completed", "expired" + ] + step_details: StepDetails + thread_id: str + type: Literal["message_creation", "tool_calls"] + usage: Usage | None + +class StepDetails(BaseModel): + """Step details (message or tool calls).""" + type: Literal["message_creation", "tool_calls"] + message_creation: dict | None + tool_calls: list[dict] | None +``` + +## Complete Example with Tool Calls + +```python +from openai import OpenAI +import json + +client = OpenAI() + +# 1. Create assistant with function +assistant = client.beta.assistants.create( + name="Weather Assistant", + instructions="Help with weather queries.", + model="gpt-4", + tools=[{ + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather for a location", + "parameters": { + "type": "object", + "properties": { + "location": {"type": "string"} + }, + "required": ["location"] + } + } + }] +) + +# 2. Create thread and message +thread = client.beta.threads.create() + +client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="What's the weather in San Francisco?" +) + +# 3. Run assistant +run = client.beta.threads.runs.create_and_poll( + thread_id=thread.id, + assistant_id=assistant.id +) + +# 4. Handle tool calls +if run.status == "requires_action": + tool_calls = run.required_action.submit_tool_outputs.tool_calls + + tool_outputs = [] + for tool_call in tool_calls: + if tool_call.function.name == "get_weather": + args = json.loads(tool_call.function.arguments) + # Call actual weather API + result = {"temp": 72, "condition": "sunny"} + + tool_outputs.append({ + "tool_call_id": tool_call.id, + "output": json.dumps(result) + }) + + # Submit and wait + run = client.beta.threads.runs.submit_tool_outputs_and_poll( + thread_id=thread.id, + run_id=run.id, + tool_outputs=tool_outputs + ) + +# 5. Get final response +messages = client.beta.threads.messages.list(thread_id=thread.id) +print(messages.data[0].content[0].text.value) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def run_assistant(): + client = AsyncOpenAI() + + run = await client.beta.threads.runs.create( + thread_id="thread_abc123", + assistant_id="asst_abc123" + ) + + while run.status not in ["completed", "failed"]: + await asyncio.sleep(1) + run = await client.beta.threads.runs.retrieve( + thread_id="thread_abc123", + run_id=run.id + ) + + return run + +run = asyncio.run(run_assistant()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/threads-messages.md b/.tessl/tiles/tessl/pypi-openai/docs/threads-messages.md new file mode 100644 index 000000000..0cd326b53 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/threads-messages.md @@ -0,0 +1,679 @@ +# Threads and Messages + +Create conversational threads and manage messages within the Assistants API. Threads maintain conversation state and messages represent user inputs and assistant responses. + +## Threads + +### Create Thread + +Create a new conversation thread. + +```python { .api } +def create( + self, + *, + messages: list[dict] | Omit = omit, + metadata: dict[str, str] | Omit = omit, + tool_resources: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Thread: + """ + Create a conversation thread. + + Args: + messages: Initial messages to add to thread. + [{"role": "user", "content": "Hello"}] + + metadata: Key-value pairs (max 16). Keys max 64 chars, values max 512 chars. + + tool_resources: Resources for tools. + - {"code_interpreter": {"file_ids": [...]}} + - {"file_search": {"vector_store_ids": [...]}} + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Thread: Created thread. + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create empty thread +thread = client.beta.threads.create() +print(f"Thread ID: {thread.id}") + +# Create with initial messages +thread = client.beta.threads.create( + messages=[ + {"role": "user", "content": "Hello!"}, + {"role": "user", "content": "How are you?"} + ] +) + +# With metadata +thread = client.beta.threads.create( + metadata={ + "user_id": "user-123", + "session": "abc" + } +) + +# With tool resources +thread = client.beta.threads.create( + tool_resources={ + "file_search": { + "vector_store_ids": ["vs_abc123"] + } + } +) +``` + +### Retrieve Thread + +Get thread details. + +```python { .api } +def retrieve( + self, + thread_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Thread: + """Get thread details.""" +``` + +### Update Thread + +Modify thread metadata. + +```python { .api } +def update( + self, + thread_id: str, + *, + metadata: dict[str, str] | Omit = omit, + tool_resources: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Thread: + """Update thread properties.""" +``` + +### Delete Thread + +Delete a thread. + +```python { .api } +def delete( + self, + thread_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> ThreadDeleted: + """Delete a thread.""" +``` + +### Create and Run + +Create thread and immediately run assistant (convenience method). + +```python { .api } +def create_and_run( + self, + *, + assistant_id: str, + instructions: str | Omit = omit, + metadata: dict[str, str] | Omit = omit, + model: str | Omit = omit, + thread: dict | Omit = omit, + tools: list[dict] | Omit = omit, + stream: bool | Omit = omit, + temperature: float | Omit = omit, + tool_choice: str | dict | Omit = omit, + top_p: float | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """ + Create thread and run assistant in one call. + + Args: + assistant_id: The assistant ID. + instructions: Override assistant instructions. + metadata: Thread metadata. + model: Override assistant model. + thread: Thread configuration including messages. + tools: Override assistant tools. + stream: Enable streaming. + temperature: Sampling temperature. + tool_choice: Tool choice configuration. + top_p: Nucleus sampling. + + Returns: + Run: Created run. + """ +``` + +Usage example: + +```python +# Create thread and run +run = client.beta.threads.create_and_run( + assistant_id="asst_abc123", + thread={ + "messages": [ + {"role": "user", "content": "Hello!"} + ] + } +) + +print(f"Run ID: {run.id}") +print(f"Thread ID: {run.thread_id}") +``` + +### Create and Run with Polling + +Create a thread, start a run, and automatically poll until the run reaches a terminal state. + +```python { .api } +def create_and_run_poll( + self, + *, + assistant_id: str, + instructions: str | Omit = omit, + max_completion_tokens: int | Omit = omit, + max_prompt_tokens: int | Omit = omit, + metadata: dict[str, str] | Omit = omit, + model: str | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: dict | Omit = omit, + temperature: float | Omit = omit, + thread: dict | Omit = omit, + tool_choice: str | dict | Omit = omit, + tool_resources: dict | Omit = omit, + tools: list[dict] | Omit = omit, + top_p: float | Omit = omit, + truncation_strategy: dict | Omit = omit, + poll_interval_ms: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Run: + """ + Create a thread, start a run, and poll for completion. + + Helper method that automatically polls the run until it reaches a terminal state + (completed, failed, cancelled, expired). More information on Run lifecycles: + https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps + + Args: + assistant_id: The assistant ID to run. + poll_interval_ms: Polling interval in milliseconds (defaults to 1000ms). + thread: Thread configuration including messages. + instructions: Override assistant instructions. + tools: Override assistant tools. + metadata: Thread metadata. + (Additional parameters same as create_and_run) + + Returns: + Run: Completed run in terminal state. + """ +``` + +Usage example: + +```python +# Create and poll automatically +run = client.beta.threads.create_and_run_poll( + assistant_id="asst_abc123", + thread={ + "messages": [{"role": "user", "content": "Explain quantum computing"}] + }, + poll_interval_ms=500 # Poll every 500ms +) + +# Run is guaranteed to be in terminal state +print(f"Status: {run.status}") # completed, failed, cancelled, or expired +``` + +### Create and Run with Streaming + +Create a thread, start a run, and stream the response back in real-time. + +```python { .api } +def create_and_run_stream( + self, + *, + assistant_id: str, + instructions: str | Omit = omit, + max_completion_tokens: int | Omit = omit, + max_prompt_tokens: int | Omit = omit, + metadata: dict[str, str] | Omit = omit, + model: str | Omit = omit, + parallel_tool_calls: bool | Omit = omit, + response_format: dict | Omit = omit, + temperature: float | Omit = omit, + thread: dict | Omit = omit, + tool_choice: str | dict | Omit = omit, + tool_resources: dict | Omit = omit, + tools: list[dict] | Omit = omit, + top_p: float | Omit = omit, + truncation_strategy: dict | Omit = omit, + event_handler: AssistantEventHandler | None = None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> AssistantStreamManager[AssistantEventHandler]: + """ + Create a thread and stream the run back in real-time. + + Args: + assistant_id: The assistant ID to run. + event_handler: Optional custom event handler for processing stream events. + thread: Thread configuration including messages. + instructions: Override assistant instructions. + (Additional parameters same as create_and_run) + + Returns: + AssistantStreamManager: Stream manager for handling assistant events. + """ +``` + +Usage example: + +```python +# Stream with default handler +with client.beta.threads.create_and_run_stream( + assistant_id="asst_abc123", + thread={ + "messages": [{"role": "user", "content": "Tell me a story"}] + } +) as stream: + for event in stream: + if event.event == 'thread.message.delta': + print(event.data.delta.content[0].text.value, end='', flush=True) + +# With custom event handler +from openai import AssistantEventHandler + +class MyHandler(AssistantEventHandler): + def on_text_delta(self, delta, snapshot): + print(delta.value, end='', flush=True) + +with client.beta.threads.create_and_run_stream( + assistant_id="asst_abc123", + thread={ + "messages": [{"role": "user", "content": "Hello"}] + }, + event_handler=MyHandler() +) as stream: + stream.until_done() +``` + +## Messages + +### Create Message + +Add a message to a thread. + +```python { .api } +def create( + self, + thread_id: str, + *, + role: Literal["user", "assistant"], + content: str | list[dict], + attachments: list[dict] | Omit = omit, + metadata: dict[str, str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Message: + """ + Add a message to a thread. + + Args: + thread_id: The thread ID. + + role: Message role. "user" or "assistant". + + content: Message content. Can be: + - String: "Hello" + - List with text/images: [{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {...}}] + + attachments: File attachments with tools. + [{"file_id": "file-abc", "tools": [{"type": "code_interpreter"}]}] + + metadata: Key-value pairs. + + Returns: + Message: Created message. + """ +``` + +Usage examples: + +```python +# Simple text message +message = client.beta.threads.messages.create( + thread_id="thread_abc123", + role="user", + content="What is the weather today?" +) + +# With attachments +message = client.beta.threads.messages.create( + thread_id="thread_abc123", + role="user", + content="Analyze this data", + attachments=[ + { + "file_id": "file-abc123", + "tools": [{"type": "code_interpreter"}] + } + ] +) + +# Multimodal message (text + image) +message = client.beta.threads.messages.create( + thread_id="thread_abc123", + role="user", + content=[ + {"type": "text", "text": "What's in this image?"}, + { + "type": "image_url", + "image_url": {"url": "https://example.com/image.jpg"} + } + ] +) + +# With metadata +message = client.beta.threads.messages.create( + thread_id="thread_abc123", + role="user", + content="Question here", + metadata={"source": "web_ui"} +) +``` + +### Retrieve Message + +Get message details. + +```python { .api } +def retrieve( + self, + thread_id: str, + message_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Message: + """Get message details.""" +``` + +### Update Message + +Modify message metadata. + +```python { .api } +def update( + self, + thread_id: str, + message_id: str, + *, + metadata: dict[str, str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Message: + """Update message metadata.""" +``` + +### List Messages + +List messages in a thread. + +```python { .api } +def list( + self, + thread_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + run_id: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[Message]: + """ + List messages in a thread. + + Args: + thread_id: The thread ID. + after: Cursor for next page. + before: Cursor for previous page. + limit: Number to retrieve (max 100). Default 20. + order: Sort order. "asc" or "desc". Default "desc" (newest first). + run_id: Filter by run ID. + + Returns: + SyncCursorPage[Message]: Paginated messages. + """ +``` + +Usage example: + +```python +# List all messages +messages = client.beta.threads.messages.list(thread_id="thread_abc123") + +for message in messages: + print(f"{message.role}: {message.content[0].text.value}") + +# List in chronological order +messages = client.beta.threads.messages.list( + thread_id="thread_abc123", + order="asc" +) + +# Pagination +page1 = client.beta.threads.messages.list(thread_id="thread_abc123", limit=10) +page2 = client.beta.threads.messages.list( + thread_id="thread_abc123", + limit=10, + after=page1.data[-1].id +) +``` + +### Delete Message + +Delete a message. + +```python { .api } +def delete( + self, + thread_id: str, + message_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> MessageDeleted: + """Delete a message.""" +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Thread(BaseModel): + """Conversation thread.""" + id: str + created_at: int + metadata: dict[str, str] | None + object: Literal["thread"] + tool_resources: dict | None + +class ThreadDeleted(BaseModel): + """Deletion confirmation.""" + id: str + deleted: bool + object: Literal["thread.deleted"] + +class Message(BaseModel): + """Thread message.""" + id: str + assistant_id: str | None + attachments: list[dict] | None + completed_at: int | None + content: list[MessageContent] + created_at: int + incomplete_at: int | None + incomplete_details: dict | None + metadata: dict[str, str] | None + object: Literal["thread.message"] + role: Literal["user", "assistant"] + run_id: str | None + status: Literal["in_progress", "incomplete", "completed"] + thread_id: str + +class MessageContent(BaseModel): + """Message content part.""" + type: Literal["text", "image_file", "image_url"] + text: TextContent | None + image_file: ImageFile | None + image_url: ImageURL | None + +class TextContent(BaseModel): + """Text content.""" + value: str + annotations: list[Annotation] + +class ImageFile(BaseModel): + """Image file reference.""" + file_id: str + detail: Literal["auto", "low", "high"] | None + +class ImageURL(BaseModel): + """Image URL reference.""" + url: str + detail: Literal["auto", "low", "high"] | None + +class Annotation(BaseModel): + """Content annotation (file citation/path).""" + type: Literal["file_citation", "file_path"] + text: str + start_index: int + end_index: int + file_citation: dict | None + file_path: dict | None + +class MessageDeleted(BaseModel): + """Deletion confirmation.""" + id: str + deleted: bool + object: Literal["thread.message.deleted"] +``` + +## Complete Example + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Create assistant +assistant = client.beta.assistants.create( + name="Helper", + model="gpt-4", + instructions="You are helpful." +) + +# 2. Create thread +thread = client.beta.threads.create() + +# 3. Add user message +message = client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="Tell me a joke" +) + +# 4. Run assistant +run = client.beta.threads.runs.create( + thread_id=thread.id, + assistant_id=assistant.id +) + +# 5. Wait for completion +import time + +while run.status not in ["completed", "failed"]: + time.sleep(1) + run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) + +# 6. Get messages +messages = client.beta.threads.messages.list(thread_id=thread.id) + +for message in messages: + role = message.role + content = message.content[0].text.value + print(f"{role}: {content}") +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_conversation(): + client = AsyncOpenAI() + + thread = await client.beta.threads.create() + + message = await client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="Hello!" + ) + + return thread.id, message.id + +thread_id, message_id = asyncio.run(create_conversation()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/uploads.md b/.tessl/tiles/tessl/pypi-openai/docs/uploads.md new file mode 100644 index 000000000..29518a950 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/uploads.md @@ -0,0 +1,466 @@ +# Uploads + +Upload large files in chunks for use with Assistants, Fine-tuning, and Batch processing. The Uploads API enables efficient multipart upload of files up to 8 GB, splitting them into 64 MB parts that can be uploaded in parallel. + +## Capabilities + +### Create Upload + +Create an intermediate Upload object that accepts multiple parts. + +```python { .api } +def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: FilePurpose, + expires_after: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Upload: + """ + Create an intermediate Upload object for adding file parts. + + Args: + bytes: Total number of bytes in the file being uploaded. + + filename: Name of the file to upload. + + mime_type: MIME type of the file. Must be supported for the specified purpose. + See https://platform.openai.com/docs/assistants/tools/file-search#supported-files + + purpose: Intended purpose of the file. Options: + - "assistants": For use with Assistants API + - "batch": For batch processing + - "fine-tune": For fine-tuning + - "vision": For vision capabilities + See https://platform.openai.com/docs/api-reference/files/create#files-create-purpose + + expires_after: Expiration policy for the file. Default: files with purpose=batch + expire after 30 days, others persist until manually deleted. + {"anchor": "created_at", "days": 7} expires 7 days after creation. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Upload: Upload object with ID to use for adding parts. + Contains status, expires_at, and other metadata. + + Notes: + - Maximum upload size: 8 GB + - Upload expires 1 hour after creation + - Must complete upload before expiration + - Each part can be at most 64 MB + """ +``` + +### Complete Upload + +Finalize the upload after all parts have been added. + +```python { .api } +def complete( + self, + upload_id: str, + *, + part_ids: list[str], + md5: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Upload: + """ + Complete the Upload and create a File object. + + Args: + upload_id: ID of the Upload to complete. + + part_ids: Ordered list of Part IDs. Order determines how parts are assembled. + + md5: Optional MD5 checksum to verify uploaded bytes match expectations. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Upload: Completed Upload object containing a nested File object + ready for use in the rest of the platform. + + Notes: + - Total bytes uploaded must match bytes specified in create() + - No parts can be added after completion + - Upload must not be cancelled or expired + """ +``` + +### Cancel Upload + +Cancel an upload that is no longer needed. + +```python { .api } +def cancel( + self, + upload_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Upload: + """ + Cancel an Upload. + + Args: + upload_id: ID of the Upload to cancel. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Upload: Cancelled Upload object with status="cancelled". + + Notes: + - No parts can be added after cancellation + - Previously uploaded parts are discarded + """ +``` + +### Upload File Chunked + +High-level helper that handles the entire upload process automatically. + +```python { .api } +def upload_file_chunked( + self, + *, + file: str | os.PathLike | bytes, + mime_type: str, + purpose: FilePurpose, + filename: str | None = None, + bytes: int | None = None, + part_size: int | None = None, + md5: str | Omit = omit, +) -> Upload: + """ + Upload a file in chunks automatically. + + This convenience method handles: + 1. Creating the Upload + 2. Splitting file into parts + 3. Uploading each part sequentially + 4. Completing the Upload + + Args: + file: File to upload. Can be: + - Path-like object: Path("my-paper.pdf") + - String path: "my-paper.pdf" + - bytes: In-memory file data (requires filename and bytes args) + + mime_type: MIME type of the file (e.g., "application/pdf"). + + purpose: Intended purpose ("assistants", "batch", "fine-tune", "vision"). + + filename: Filename (required if file is bytes, optional otherwise). + + bytes: Total file size in bytes (required if file is bytes, optional otherwise). + If not provided for path, automatically determined from file. + + part_size: Size of each part in bytes. Default: 64 MB (64 * 1024 * 1024). + Each part uploads as a separate request. + + md5: Optional MD5 checksum for verification. + + Returns: + Upload: Completed Upload object containing the File. + + Raises: + TypeError: If filename or bytes not provided for in-memory files. + ValueError: If file path is invalid or file cannot be read. + """ +``` + +Usage examples: + +```python +from pathlib import Path +from openai import OpenAI + +client = OpenAI() + +# Upload a file from disk (simplest approach) +upload = client.uploads.upload_file_chunked( + file=Path("training_data.jsonl"), + mime_type="application/jsonl", + purpose="fine-tune" +) + +print(f"Upload complete! File ID: {upload.file.id}") + +# Upload with custom part size (e.g., 32 MB parts) +upload = client.uploads.upload_file_chunked( + file="large_dataset.jsonl", + mime_type="application/jsonl", + purpose="batch", + part_size=32 * 1024 * 1024 +) + +# Upload in-memory bytes +file_data = b"..." # Your file data +upload = client.uploads.upload_file_chunked( + file=file_data, + filename="document.pdf", + bytes=len(file_data), + mime_type="application/pdf", + purpose="assistants" +) + +# Upload with MD5 verification +upload = client.uploads.upload_file_chunked( + file="important_data.csv", + mime_type="text/csv", + purpose="assistants", + md5="5d41402abc4b2a76b9719d911017c592" +) +``` + +### Create Part + +Add a single part to an Upload. + +```python { .api } +def create( + self, + upload_id: str, + *, + data: FileTypes, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> UploadPart: + """ + Add a Part to an Upload. + + Args: + upload_id: ID of the Upload to add this Part to. + + data: Chunk of bytes for this Part. Maximum 64 MB. + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + UploadPart: Part object with ID to use when completing the Upload. + + Notes: + - Each Part can be at most 64 MB + - Total size across all parts cannot exceed 8 GB + - Parts can be added in parallel for faster uploads + - Order is determined when completing the Upload + """ +``` + +Advanced manual upload example: + +```python +from openai import OpenAI +import io + +client = OpenAI() + +# Step 1: Create the Upload +file_path = "large_file.pdf" +file_size = os.path.getsize(file_path) + +upload = client.uploads.create( + bytes=file_size, + filename="large_file.pdf", + mime_type="application/pdf", + purpose="assistants" +) + +print(f"Created upload: {upload.id}") + +# Step 2: Upload parts +part_size = 64 * 1024 * 1024 # 64 MB +part_ids = [] + +with open(file_path, "rb") as f: + while True: + chunk = f.read(part_size) + if not chunk: + break + + part = client.uploads.parts.create( + upload_id=upload.id, + data=chunk + ) + part_ids.append(part.id) + print(f"Uploaded part {len(part_ids)}: {part.id}") + +# Step 3: Complete the Upload +completed = client.uploads.complete( + upload_id=upload.id, + part_ids=part_ids +) + +print(f"Upload complete! File ID: {completed.file.id}") + +# Handle errors by cancelling +try: + # ... upload process ... + pass +except Exception as e: + print(f"Error during upload: {e}") + client.uploads.cancel(upload_id=upload.id) + print("Upload cancelled") +``` + +Parallel upload example: + +```python +from openai import OpenAI +from concurrent.futures import ThreadPoolExecutor +import io + +client = OpenAI() + +def upload_part(upload_id: str, part_data: bytes) -> str: + """Upload a single part and return its ID.""" + part = client.uploads.parts.create( + upload_id=upload_id, + data=part_data + ) + return part.id + +# Create upload +file_path = "large_file.pdf" +file_size = os.path.getsize(file_path) + +upload = client.uploads.create( + bytes=file_size, + filename="large_file.pdf", + mime_type="application/pdf", + purpose="assistants" +) + +# Split file into chunks +part_size = 64 * 1024 * 1024 +chunks = [] + +with open(file_path, "rb") as f: + while True: + chunk = f.read(part_size) + if not chunk: + break + chunks.append(chunk) + +# Upload parts in parallel +with ThreadPoolExecutor(max_workers=4) as executor: + part_ids = list(executor.map( + lambda chunk: upload_part(upload.id, chunk), + chunks + )) + +# Complete upload +completed = client.uploads.complete( + upload_id=upload.id, + part_ids=part_ids +) + +print(f"Parallel upload complete! File ID: {completed.file.id}") +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def upload_file(): + client = AsyncOpenAI() + + # Async upload + upload = await client.uploads.upload_file_chunked( + file="data.jsonl", + mime_type="application/jsonl", + purpose="fine-tune" + ) + + return upload.file.id + +file_id = asyncio.run(upload_file()) +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Upload(BaseModel): + """Upload object containing metadata and status.""" + id: str + bytes: int + created_at: int + expires_at: int + filename: str + object: Literal["upload"] + purpose: FilePurpose + status: Literal["pending", "completed", "cancelled", "expired"] + file: FileObject | None # Present when status="completed" + +class UploadPart(BaseModel): + """Part object representing a chunk of an upload.""" + id: str + created_at: int + object: Literal["upload.part"] + upload_id: str + +FilePurpose = Literal["assistants", "batch", "fine-tune", "vision"] + +FileTypes = Union[ + FileContent, + Tuple[Optional[str], FileContent], + Tuple[Optional[str], FileContent, Optional[str]] +] + +class Omit: + """Sentinel value for omitted parameters.""" +``` + +## Access Pattern + +```python +# Synchronous +from openai import OpenAI +client = OpenAI() +client.uploads.create(...) +client.uploads.complete(...) +client.uploads.cancel(...) +client.uploads.upload_file_chunked(...) +client.uploads.parts.create(...) + +# Asynchronous +from openai import AsyncOpenAI +client = AsyncOpenAI() +await client.uploads.create(...) +await client.uploads.complete(...) +await client.uploads.cancel(...) +await client.uploads.upload_file_chunked(...) +await client.uploads.parts.create(...) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/vector-stores.md b/.tessl/tiles/tessl/pypi-openai/docs/vector-stores.md new file mode 100644 index 000000000..e45582cc9 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/vector-stores.md @@ -0,0 +1,1146 @@ +# Vector Stores + +Create and manage vector stores for semantic search and retrieval with the Assistants API. Vector stores enable file search capabilities by storing and indexing documents for efficient retrieval. + +## Capabilities + +### Create Vector Store + +Create a new vector store for storing and searching documents. + +```python { .api } +def create( + self, + *, + chunking_strategy: dict | Omit = omit, + description: str | Omit = omit, + expires_after: dict | Omit = omit, + file_ids: list[str] | Omit = omit, + metadata: dict[str, str] | Omit = omit, + name: str | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStore: + """ + Create a vector store for file search. + + Args: + chunking_strategy: How to chunk files. Options: + - {"type": "auto"}: Automatic chunking (default) + - {"type": "static", "static": {"max_chunk_size_tokens": 800, "chunk_overlap_tokens": 400}} + + description: Description of the vector store (optional). + + expires_after: Expiration policy. Options: + - {"anchor": "last_active_at", "days": 7}: Expires 7 days after last use + - {"anchor": "last_active_at", "days": 1}: Expires 1 day after last use + + file_ids: List of file IDs to add to the vector store (max 10000). + Files must have purpose="assistants". + + metadata: Key-value pairs for storing additional info (max 16). + Keys max 64 chars, values max 512 chars. + + name: Name of the vector store (optional). + + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStore: Created vector store. + + Raises: + BadRequestError: Invalid parameters or too many files + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create empty vector store +vector_store = client.beta.vector_stores.create( + name="Product Documentation" +) + +print(f"Vector Store ID: {vector_store.id}") + +# Create with files +file_ids = ["file-abc123", "file-def456"] + +vector_store = client.beta.vector_stores.create( + name="Knowledge Base", + file_ids=file_ids +) + +# With expiration policy +vector_store = client.beta.vector_stores.create( + name="Temporary Store", + expires_after={"anchor": "last_active_at", "days": 7} +) + +# With custom chunking +vector_store = client.beta.vector_stores.create( + name="Custom Chunking", + file_ids=file_ids, + chunking_strategy={ + "type": "static", + "static": { + "max_chunk_size_tokens": 800, + "chunk_overlap_tokens": 400 + } + } +) + +# With metadata +vector_store = client.beta.vector_stores.create( + name="Project Docs", + metadata={ + "project": "alpha", + "version": "1.0" + } +) +``` + +### Retrieve Vector Store + +Get vector store details. + +```python { .api } +def retrieve( + self, + vector_store_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStore: + """ + Retrieve vector store details. + + Args: + vector_store_id: The ID of the vector store. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStore: Vector store details. + + Raises: + NotFoundError: Vector store not found + """ +``` + +Usage example: + +```python +# Get vector store +store = client.beta.vector_stores.retrieve("vs_abc123") + +print(f"Name: {store.name}") +print(f"File counts: {store.file_counts}") +print(f"Status: {store.status}") +``` + +### Update Vector Store + +Modify vector store settings. + +```python { .api } +def update( + self, + vector_store_id: str, + *, + name: str | Omit = omit, + expires_after: dict | Omit = omit, + metadata: dict[str, str] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStore: + """ + Update vector store properties. + + Args: + vector_store_id: The ID of the vector store. + name: New name for the vector store. + expires_after: New expiration policy. + metadata: New metadata (replaces existing). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStore: Updated vector store. + """ +``` + +Usage example: + +```python +# Update name +store = client.beta.vector_stores.update( + "vs_abc123", + name="Updated Documentation" +) + +# Update metadata +store = client.beta.vector_stores.update( + "vs_abc123", + metadata={"version": "2.0"} +) +``` + +### List Vector Stores + +List all vector stores with pagination. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + before: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[VectorStore]: + """ + List vector stores with pagination. + + Args: + after: Cursor for next page. + before: Cursor for previous page. + limit: Number to retrieve (max 100). Default 20. + order: Sort order. "asc" or "desc". Default "desc". + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[VectorStore]: Paginated list of vector stores. + """ +``` + +Usage example: + +```python +# List all stores +stores = client.beta.vector_stores.list() + +for store in stores: + print(f"{store.name} ({store.id})") + +# Pagination +page1 = client.beta.vector_stores.list(limit=10) +page2 = client.beta.vector_stores.list(limit=10, after=page1.data[-1].id) +``` + +### Delete Vector Store + +Delete a vector store and all its files. + +```python { .api } +def delete( + self, + vector_store_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreDeleted: + """ + Delete a vector store. + + Args: + vector_store_id: The ID of the vector store to delete. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreDeleted: Deletion confirmation. + + Raises: + NotFoundError: Vector store not found + """ +``` + +Usage example: + +```python +# Delete vector store +result = client.beta.vector_stores.delete("vs_abc123") + +print(f"Deleted: {result.deleted}") +``` + +### Add Files to Vector Store + +Add files to an existing vector store. + +```python { .api } +def create( + self, + vector_store_id: str, + *, + file_id: str, + attributes: dict[str, str | float | bool] | None | Omit = omit, + chunking_strategy: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFile: + """ + Add a file to a vector store. + + Args: + vector_store_id: The vector store ID. + file_id: The file ID to add. + attributes: Key-value pairs that can be attached to the file (max 16 pairs). + Keys: max 64 chars. Values: max 512 chars (strings) or numbers/booleans. + Useful for storing metadata like version numbers, categories, etc. + chunking_strategy: Chunking configuration (same as vector store create). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFile: Added file details. + """ +``` + +Usage example: + +```python +# Add file to vector store +file = client.beta.vector_stores.files.create( + vector_store_id="vs_abc123", + file_id="file-xyz789" +) + +print(f"File status: {file.status}") +``` + +### Retrieve Vector Store File + +Get details about a file in a vector store. + +```python { .api } +def retrieve( + self, + file_id: str, + *, + vector_store_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFile: + """ + Retrieve details about a vector store file. + + Args: + file_id: The ID of the file. + vector_store_id: The vector store ID. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFile: File details including status and metadata. + + Raises: + NotFoundError: File not found in vector store + """ +``` + +Usage example: + +```python +# Get file details +file = client.beta.vector_stores.files.retrieve( + file_id="file-xyz789", + vector_store_id="vs_abc123" +) + +print(f"Status: {file.status}") +print(f"Usage bytes: {file.usage_bytes}") +``` + +### Update Vector Store File + +Update attributes on a vector store file. + +```python { .api } +def update( + self, + file_id: str, + *, + vector_store_id: str, + attributes: dict[str, str | float | bool] | None, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFile: + """ + Update attributes on a vector store file. + + Args: + file_id: The ID of the file. + vector_store_id: The vector store ID. + attributes: Key-value pairs to attach (max 16 pairs). + Keys: max 64 chars, Values: max 512 chars (or numbers/booleans). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFile: Updated file object. + """ +``` + +Usage example: + +```python +# Update file attributes +file = client.beta.vector_stores.files.update( + file_id="file-xyz789", + vector_store_id="vs_abc123", + attributes={ + "category": "documentation", + "version": "1.2.0", + "priority": 5 + } +) +``` + +### List Vector Store Files + +List all files in a vector store with pagination and filtering. + +```python { .api } +def list( + self, + vector_store_id: str, + *, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[VectorStoreFile]: + """ + List files in a vector store with optional filtering. + + Args: + vector_store_id: The vector store ID. + after: Cursor for pagination (object ID to start after). + before: Cursor for pagination (object ID to start before). + filter: Filter by file status: "in_progress", "completed", "failed", "cancelled". + limit: Number of files to return (1-100, default 20). + order: Sort order by created_at: "asc" or "desc". + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[VectorStoreFile]: Paginated list of files. + """ +``` + +Usage examples: + +```python +# List all files +files = client.beta.vector_stores.files.list( + vector_store_id="vs_abc123" +) + +for file in files: + print(f"{file.id}: {file.status}") + +# Filter by status +completed_files = client.beta.vector_stores.files.list( + vector_store_id="vs_abc123", + filter="completed" +) + +# Pagination +page1 = client.beta.vector_stores.files.list( + vector_store_id="vs_abc123", + limit=10, + order="desc" +) + +page2 = client.beta.vector_stores.files.list( + vector_store_id="vs_abc123", + limit=10, + after=page1.data[-1].id +) +``` + +### Delete Vector Store File + +Remove a file from a vector store (does not delete the file itself). + +```python { .api } +def delete( + self, + file_id: str, + *, + vector_store_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFileDeleted: + """ + Delete a vector store file. + + This removes the file from the vector store but does not delete the file + itself. To delete the file, use client.files.delete(). + + Args: + file_id: The ID of the file. + vector_store_id: The vector store ID. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFileDeleted: Deletion confirmation. + """ +``` + +Usage example: + +```python +# Remove file from vector store +deleted = client.beta.vector_stores.files.delete( + file_id="file-xyz789", + vector_store_id="vs_abc123" +) + +print(f"Deleted: {deleted.id}") + +# To also delete the file itself: +client.files.delete(file_id="file-xyz789") +``` + +### Get Vector Store File Content + +Retrieve the parsed contents of a vector store file. + +```python { .api } +def content( + self, + file_id: str, + *, + vector_store_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncPage[FileContentResponse]: + """ + Retrieve the parsed contents of a vector store file. + + Args: + file_id: The ID of the file. + vector_store_id: The vector store ID. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncPage[FileContentResponse]: Parsed file content with chunks. + """ +``` + +Usage example: + +```python +# Get parsed file content +content_pages = client.beta.vector_stores.files.content( + file_id="file-xyz789", + vector_store_id="vs_abc123" +) + +for page in content_pages: + print(f"Content: {page.content}") + print(f"Metadata: {page.metadata}") +``` + +### Helper: Create and Poll + +Convenience method that combines create() and poll() - adds a file and waits for processing. + +```python { .api } +def create_and_poll( + self, + file_id: str, + *, + vector_store_id: str, + attributes: dict[str, str | float | bool] | None | Omit = omit, + poll_interval_ms: int | Omit = omit, + chunking_strategy: dict | Omit = omit, +) -> VectorStoreFile: + """ + Attach a file to the given vector store and wait for it to be processed. + + Args: + file_id: The file ID to add. + vector_store_id: The vector store ID. + attributes: Key-value pairs to attach to the file. + poll_interval_ms: Polling interval in milliseconds. If not specified, uses server-suggested interval. + chunking_strategy: Chunking configuration. + + Returns: + VectorStoreFile: Processed file details (may be completed or failed). + """ +``` + +Usage example: + +```python +# Add file and wait for processing +file = client.beta.vector_stores.files.create_and_poll( + file_id="file-xyz789", + vector_store_id="vs_abc123", + poll_interval_ms=1000 +) + +print(f"Final status: {file.status}") +if file.status == "failed": + print(f"Error: {file.last_error}") +``` + +### Helper: Poll Processing + +Wait for a vector store file to finish processing. + +```python { .api } +def poll( + self, + file_id: str, + *, + vector_store_id: str, + poll_interval_ms: int | Omit = omit, +) -> VectorStoreFile: + """ + Wait for the vector store file to finish processing. + + Note: this will return even if the file failed to process. Check + file.status and file.last_error to handle failures. + + Args: + file_id: The file ID. + vector_store_id: The vector store ID. + poll_interval_ms: Polling interval in milliseconds. If not specified, uses server-suggested interval. + + Returns: + VectorStoreFile: File details after processing completes (or fails). + """ +``` + +Usage example: + +```python +# First create the file +file = client.beta.vector_stores.files.create( + file_id="file-xyz789", + vector_store_id="vs_abc123" +) + +# Then poll until processing completes +processed_file = client.beta.vector_stores.files.poll( + file_id="file-xyz789", + vector_store_id="vs_abc123" +) + +print(f"Status: {processed_file.status}") +``` + +### Helper: Upload and Attach + +Upload a new file to the Files API and attach it to the vector store. + +```python { .api } +def upload( + self, + *, + vector_store_id: str, + file: FileTypes, + chunking_strategy: dict | Omit = omit, +) -> VectorStoreFile: + """ + Upload a file to the Files API and attach it to the given vector store. + + Note: The file will be asynchronously processed. Use upload_and_poll() + to wait for processing to complete. + + Args: + vector_store_id: The vector store ID. + file: File to upload (path, file object, or bytes). + chunking_strategy: Chunking configuration. + + Returns: + VectorStoreFile: File details (status will be "in_progress"). + """ +``` + +Usage example: + +```python +# Upload and attach file +with open("document.pdf", "rb") as f: + file = client.beta.vector_stores.files.upload( + vector_store_id="vs_abc123", + file=f + ) + +print(f"Uploaded file ID: {file.id}") +print(f"Status: {file.status}") +``` + +### Helper: Upload and Poll + +Complete workflow - upload a file, attach to vector store, and wait for processing. + +```python { .api } +def upload_and_poll( + self, + *, + vector_store_id: str, + file: FileTypes, + attributes: dict[str, str | float | bool] | None | Omit = omit, + poll_interval_ms: int | Omit = omit, + chunking_strategy: dict | Omit = omit, +) -> VectorStoreFile: + """ + Upload a file and poll until processing is complete. + + This is the most convenient method for adding files - it handles + the upload, attachment, and waiting in one call. + + Args: + vector_store_id: The vector store ID. + file: File to upload (path, file object, or bytes). + attributes: Key-value pairs to attach to the file. + poll_interval_ms: Polling interval in milliseconds. + chunking_strategy: Chunking configuration. + + Returns: + VectorStoreFile: Processed file details (may be completed or failed). + """ +``` + +Usage example: + +```python +# Complete workflow in one call +with open("document.pdf", "rb") as f: + file = client.beta.vector_stores.files.upload_and_poll( + vector_store_id="vs_abc123", + file=f, + attributes={"type": "documentation", "version": "2.0"}, + poll_interval_ms=1000 + ) + +print(f"File ID: {file.id}") +print(f"Status: {file.status}") +print(f"Usage bytes: {file.usage_bytes}") + +if file.status == "failed": + print(f"Error: {file.last_error}") +``` + +### Search Vector Store + +Search for relevant content in a vector store based on a query and optional file attributes filter. + +```python { .api } +def search( + self, + vector_store_id: str, + *, + query: str | list[str], + filters: dict | Omit = omit, + max_num_results: int | Omit = omit, + ranking_options: dict | Omit = omit, + rewrite_query: bool | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncPage[VectorStoreSearchResponse]: + """ + Search vector store for relevant content. + + Args: + vector_store_id: The vector store ID. + query: Search query text (string or list of strings). + filters: A filter to apply based on file attributes. + max_num_results: Maximum number of results to return (1-50 inclusive). + ranking_options: Ranking options for search. + rewrite_query: Whether to rewrite the natural language query for vector search. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncPage[VectorStoreSearchResponse]: Paginated search results with relevant chunks. + """ +``` + +Usage example: + +```python +# Search vector store +results = client.beta.vector_stores.search( + vector_store_id="vs_abc123", + query="How do I install the SDK?", + max_num_results=5 +) + +for result in results.data: + print(f"Score: {result.score}") + print(f"Content: {result.content}") + print(f"File: {result.file_id}") +``` + +### File Batches + +Batch operations for adding multiple files to a vector store efficiently. Accessed via `client.beta.vector_stores.file_batches`. + +```python { .api } +def create( + self, + vector_store_id: str, + *, + file_ids: list[str] | Omit = omit, + files: list[dict] | Omit = omit, + attributes: dict[str, str | float | bool] | None | Omit = omit, + chunking_strategy: dict | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFileBatch: + """ + Create a batch of files to add to vector store. + + Args: + vector_store_id: The vector store ID. + file_ids: List of file IDs to add (mutually exclusive with files). + files: List of file objects with per-file metadata (mutually exclusive with file_ids). + attributes: Metadata to apply to all files in batch. + chunking_strategy: Strategy for chunking files. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFileBatch: Created batch object. + """ + +def retrieve( + self, + batch_id: str, + *, + vector_store_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFileBatch: + """ + Retrieve file batch status. + + Args: + batch_id: The file batch ID. + vector_store_id: The vector store ID. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFileBatch: Batch details. + """ + +def cancel( + self, + batch_id: str, + *, + vector_store_id: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VectorStoreFileBatch: + """ + Cancel an in-progress file batch. + + Args: + batch_id: The file batch ID. + vector_store_id: The vector store ID. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + VectorStoreFileBatch: Updated batch with cancelled status. + """ + +def list_files( + self, + batch_id: str, + *, + vector_store_id: str, + after: str | Omit = omit, + before: str | Omit = omit, + filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncCursorPage[VectorStoreFile]: + """ + List files in a vector store batch with pagination and filtering. + + Args: + batch_id: The file batch ID to list files from. + vector_store_id: The vector store ID. + after: Cursor for pagination. Return files after this file ID. + before: Cursor for pagination. Return files before this file ID. + filter: Filter by file status. Options: + - "in_progress": Files currently processing + - "completed": Successfully processed files + - "failed": Files that failed processing + - "cancelled": Cancelled files + limit: Number of files to retrieve. Default 20, max 100. + order: Sort order. "asc" for ascending, "desc" for descending. Default "desc". + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncCursorPage[VectorStoreFile]: Paginated list of files in the batch. + """ +``` + +Usage examples: + +```python +# Create file batch +batch = client.beta.vector_stores.file_batches.create( + vector_store_id="vs_abc123", + file_ids=["file-1", "file-2", "file-3"] +) + +print(f"Batch ID: {batch.id}") +print(f"Status: {batch.status}") + +# Check batch status +batch = client.beta.vector_stores.file_batches.retrieve( + batch_id=batch.id, + vector_store_id="vs_abc123" +) + +# List files in batch +files = client.beta.vector_stores.file_batches.list_files( + batch_id=batch.id, + vector_store_id="vs_abc123" +) + +for file in files.data: + print(f"File ID: {file.id}, Status: {file.status}") + +# Filter by status +completed_files = client.beta.vector_stores.file_batches.list_files( + batch_id=batch.id, + vector_store_id="vs_abc123", + filter="completed" +) + +# Cancel batch if needed +batch = client.beta.vector_stores.file_batches.cancel( + batch_id=batch.id, + vector_store_id="vs_abc123" +) +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class VectorStore(BaseModel): + """Vector store for file search.""" + id: str + created_at: int + name: str + usage_bytes: int + file_counts: FileCounts + status: Literal["expired", "in_progress", "completed"] + expires_after: dict | None + expires_at: int | None + last_active_at: int | None + metadata: dict[str, str] | None + object: Literal["vector_store"] + +class FileCounts(BaseModel): + """File count statistics.""" + in_progress: int + completed: int + failed: int + cancelled: int + total: int + +class VectorStoreDeleted(BaseModel): + """Deletion confirmation.""" + id: str + deleted: bool + object: Literal["vector_store.deleted"] + +class VectorStoreFile(BaseModel): + """File in vector store.""" + id: str + created_at: int + vector_store_id: str + usage_bytes: int + status: Literal["in_progress", "completed", "cancelled", "failed"] + last_error: dict | None + chunking_strategy: dict | None + object: Literal["vector_store.file"] + +class VectorStoreFileBatch(BaseModel): + """Batch of files being added to vector store.""" + id: str + created_at: int + vector_store_id: str + status: Literal["in_progress", "completed", "cancelled", "failed"] + file_counts: FileCounts + object: Literal["vector_store.files_batch"] + +class VectorStoreSearchResponse(BaseModel): + """Search results.""" + data: list[SearchResult] + object: str + +class SearchResult(BaseModel): + """Single search result.""" + content: str + file_id: str + score: float + metadata: dict | None +``` + +## Best Practices + +```python +from openai import OpenAI + +client = OpenAI() + +# 1. Create vector store with appropriate files +# Upload files first +file_ids = [] +for doc_path in ["doc1.pdf", "doc2.txt", "doc3.md"]: + with open(doc_path, "rb") as f: + file = client.files.create(file=f, purpose="assistants") + file_ids.append(file.id) + +# Create vector store +store = client.beta.vector_stores.create( + name="Product Documentation", + file_ids=file_ids +) + +# 2. Wait for processing +import time + +while store.status == "in_progress": + time.sleep(2) + store = client.beta.vector_stores.retrieve(store.id) + +print(f"Status: {store.status}") +print(f"Completed files: {store.file_counts.completed}") + +# 3. Use with Assistant +assistant = client.beta.assistants.create( + name="Documentation Assistant", + instructions="Help users find information in documentation.", + model="gpt-4", + tools=[{"type": "file_search"}], + tool_resources={ + "file_search": { + "vector_store_ids": [store.id] + } + } +) + +# 4. Clean up expired stores +stores = client.beta.vector_stores.list() +for store in stores: + if store.status == "expired": + client.beta.vector_stores.delete(store.id) +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_store(): + client = AsyncOpenAI() + + store = await client.beta.vector_stores.create( + name="Async Store", + file_ids=["file-abc123"] + ) + + return store.id + +store_id = asyncio.run(create_store()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/videos.md b/.tessl/tiles/tessl/pypi-openai/docs/videos.md new file mode 100644 index 000000000..bb017aaaa --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/videos.md @@ -0,0 +1,361 @@ +# Videos + +Generate and manage videos using video generation models. Create videos from text prompts, retrieve video status, and manage video files. + +## Capabilities + +### Create Video + +Generate a video from a text prompt. + +```python { .api } +def create( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: + """ + Create a video generation request. + + Args: + prompt: Text description of the desired video. + input_reference: Optional image reference that guides generation (FileTypes). + model: Video model to use (e.g., "sora-2"). Defaults to "sora-2". + seconds: Clip duration in seconds. Defaults to 4 seconds. + size: Output resolution formatted as width x height (e.g., "720x1280"). + Defaults to "720x1280". + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Video: Created video object with initial status. + """ +``` + +### Retrieve Video + +Get video status and details. + +```python { .api } +def retrieve( + self, + video_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: + """Retrieve video details and status.""" +``` + +### Poll for Completion + +Poll a video until it finishes processing (either completed or failed status). + +```python { .api } +def poll( + self, + video_id: str, + *, + poll_interval_ms: int | Omit = omit, +) -> Video: + """ + Wait for a video to finish processing. + + This method blocks until the video reaches "completed" or "failed" status. + It automatically retries with appropriate polling intervals. + + Args: + video_id: The ID of the video to poll. + poll_interval_ms: Custom polling interval in milliseconds. If not provided, + uses server-suggested interval or defaults to 1000ms. + + Returns: + Video: The video object with final status ("completed" or "failed"). + + Note: + This method returns even if the video failed to process. Check + video.status and video.error to handle failed cases. + """ +``` + +### List Videos + +List all videos with pagination. + +```python { .api } +def list( + self, + *, + after: str | Omit = omit, + limit: int | Omit = omit, + order: Literal["asc", "desc"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> SyncConversationCursorPage[Video]: + """ + List videos with pagination. + + Args: + after: Identifier for the last item from the previous pagination request. + limit: Number of items to retrieve. + order: Sort order by timestamp ("asc" or "desc"). + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + SyncConversationCursorPage[Video]: Paginated list of videos. + """ +``` + +### Delete Video + +Delete a video. + +```python { .api } +def delete( + self, + video_id: str, + *, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> VideoDeleteResponse: + """Delete a video.""" +``` + +### Download Video Content + +Download the generated video file. + +```python { .api } +def download_content( + self, + video_id: str, + *, + variant: Literal["video", "thumbnail", "spritesheet"] | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> HttpxBinaryResponseContent: + """ + Download video content as binary data. + + Args: + video_id: The ID of the video to download. + variant: The type of content to download. Options: + - "video": The full video file (default) + - "thumbnail": A thumbnail image of the video + - "spritesheet": A spritesheet of video frames + + Returns: + HttpxBinaryResponseContent: Binary video file content. + """ +``` + +### Remix Video + +Create a new video by remixing an existing video with a new prompt. + +```python { .api } +def remix( + self, + video_id: str, + *, + prompt: str, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: + """ + Create a remix of an existing video with a new prompt. + + Remixing uses the original video's model, duration, and size settings. + Only the prompt can be changed to create variations. + + Args: + video_id: The ID of the video to remix. + prompt: New creative prompt to guide the remix. + + Returns: + Video: Remix video object (status "queued" initially). + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create initial video +video = client.videos.create_and_poll( + prompt="A serene sunset over mountains" +) + +# Download the generated video +content = client.videos.download_content(video.id) +with open("sunset.mp4", "wb") as f: + f.write(content.content) + +# Create a remix with different prompt +remix = client.videos.remix( + video.id, + prompt="Transform the sunset to a dramatic thunderstorm" +) + +# Poll for remix completion +while remix.status != "completed": + time.sleep(5) + remix = client.videos.retrieve(remix.id) + +# Download remix +remix_content = client.videos.download_content(remix.id) +with open("storm.mp4", "wb") as f: + f.write(remix_content.content) +``` + +### Create and Poll + +Create video and wait for completion. + +```python { .api } +def create_and_poll( + self, + *, + prompt: str, + input_reference: FileTypes | Omit = omit, + model: VideoModel | Omit = omit, + seconds: VideoSeconds | Omit = omit, + size: VideoSize | Omit = omit, + poll_interval_ms: int | Omit = omit, + extra_headers: dict[str, str] | None = None, + extra_query: dict[str, object] | None = None, + extra_body: dict[str, object] | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, +) -> Video: + """ + Create video and poll until generation completes. + + Args: + prompt: Text description of the desired video. + input_reference: Optional image reference that guides generation. + model: Video model to use. Defaults to "sora-2". + seconds: Clip duration in seconds. + size: Output resolution. + poll_interval_ms: Polling interval in milliseconds. + extra_headers: Additional HTTP headers. + extra_query: Additional query parameters. + extra_body: Additional JSON fields. + timeout: Request timeout in seconds. + + Returns: + Video: Completed video object. + """ +``` + +Usage examples: + +```python +from openai import OpenAI + +client = OpenAI() + +# Create video +video = client.videos.create( + prompt="A serene mountain landscape at sunset", + model="sora-2", + seconds=5, + size="720x1280" +) + +print(f"Video ID: {video.id}") +print(f"Status: {video.status}") + +# Create and wait for completion +video = client.videos.create_and_poll( + prompt="Waves crashing on a beach", + model="sora-2" +) + +print(f"Final status: {video.status}") +if video.status == "completed" and video.url: + print(f"Video URL: {video.url}") + +# Check status +video = client.videos.retrieve("video_abc123") + +# List videos +videos = client.videos.list(limit=10) + +for video in videos.data: + print(f"{video.id}: {video.status}") + +# Delete video +result = client.videos.delete("video_abc123") +print(f"Deleted: {result.deleted}") +``` + +## Types + +```python { .api } +from typing import Literal +from pydantic import BaseModel + +class Video(BaseModel): + """Video object.""" + id: str + created_at: int + model: str + prompt: str + status: Literal["queued", "processing", "completed", "failed"] + url: str | None # Available when completed + error: dict | None # Present if failed + duration: int | None + size: str | None + +class VideoDeleteResponse(BaseModel): + """Deletion confirmation.""" + id: str + deleted: bool + +VideoModel = Literal["sora-2", "sora-2-pro"] +``` + +## Async Usage + +```python +import asyncio +from openai import AsyncOpenAI + +async def create_video(): + client = AsyncOpenAI() + + video = await client.videos.create( + model="video-gen-1", + prompt="A futuristic city" + ) + + return video.id + +video_id = asyncio.run(create_video()) +``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/webhooks.md b/.tessl/tiles/tessl/pypi-openai/docs/webhooks.md new file mode 100644 index 000000000..c3e1e504d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/docs/webhooks.md @@ -0,0 +1,503 @@ +# Webhooks + +Verify and handle webhook events from OpenAI for asynchronous notifications about fine-tuning jobs, batch completions, and other events. + +## Capabilities + +### Verify Webhook Signature + +Verify that a webhook request came from OpenAI. + +```python { .api } +def verify_signature( + payload: str | bytes, + headers: dict[str, str] | list[tuple[str, str]], + *, + secret: str | None = None, + tolerance: int = 300 +) -> None: + """ + Verify webhook signature to ensure authenticity. + + Args: + payload: Raw request body (bytes or string). + headers: Request headers containing webhook-signature, webhook-timestamp, + and webhook-id. Can be dict or list of tuples. + secret: Webhook secret from OpenAI dashboard. If None, uses client's + webhook_secret or OPENAI_WEBHOOK_SECRET environment variable. + tolerance: Maximum age of webhook in seconds (default: 300). + Used to prevent replay attacks. + + Returns: + None: If signature is valid. + + Raises: + InvalidWebhookSignatureError: If signature is invalid or timestamp is too old/new. + ValueError: If secret is not provided and not set on client. + """ +``` + +Usage example: + +```python +from openai import OpenAI, InvalidWebhookSignatureError + +client = OpenAI(webhook_secret="your-webhook-secret") + +# In your webhook endpoint +def webhook_handler(request): + payload = request.body # Raw bytes + headers = request.headers # Headers dict or list + + try: + # Verify signature + client.webhooks.verify_signature( + payload=payload, + headers=headers, + secret="your-webhook-secret" # Optional if set on client + ) + + # Signature valid, process webhook + event = json.loads(payload) + handle_webhook_event(event) + + except InvalidWebhookSignatureError: + # Invalid signature, reject request + return {"error": "Invalid signature"}, 401 +``` + +### Unwrap Webhook Event + +Verify signature and parse webhook event in one call. + +```python { .api } +def unwrap( + payload: str | bytes, + headers: dict[str, str] | list[tuple[str, str]], + *, + secret: str | None = None +) -> UnwrapWebhookEvent: + """ + Verify signature and parse webhook event. + + Args: + payload: Raw request body (bytes or string). + headers: Request headers containing webhook-signature, webhook-timestamp, + and webhook-id. Can be dict or list of tuples. + secret: Webhook secret from OpenAI dashboard. If None, uses client's + webhook_secret or OPENAI_WEBHOOK_SECRET environment variable. + + Returns: + UnwrapWebhookEvent: Parsed and verified webhook event. + + Raises: + InvalidWebhookSignatureError: If signature is invalid. + ValueError: If secret is not provided and not set on client. + """ +``` + +Usage example: + +```python +from openai import OpenAI + +client = OpenAI(webhook_secret="your-webhook-secret") + +# In webhook endpoint +def webhook_handler(request): + try: + # Verify and parse in one call + event = client.webhooks.unwrap( + payload=request.body, + headers=request.headers, + secret="your-webhook-secret" # Optional if set on client + ) + + # Handle different event types + if event.type == "fine_tuning.job.succeeded": + handle_fine_tuning_success(event.data) + + elif event.type == "batch.completed": + handle_batch_completion(event.data) + + return {"status": "ok"}, 200 + + except Exception as e: + return {"error": str(e)}, 400 +``` + +## Webhook Event Types + +### Fine-tuning Events + +```python +# Job succeeded +{ + "type": "fine_tuning.job.succeeded", + "data": { + "id": "ftjob-abc123", + "fine_tuned_model": "ft:gpt-3.5-turbo:org:model:abc", + "status": "succeeded" + } +} + +# Job failed +{ + "type": "fine_tuning.job.failed", + "data": { + "id": "ftjob-abc123", + "status": "failed", + "error": {...} + } +} + +# Job cancelled +{ + "type": "fine_tuning.job.cancelled", + "data": { + "id": "ftjob-abc123", + "status": "cancelled" + } +} +``` + +### Batch Events + +```python +# Batch completed +{ + "type": "batch.completed", + "data": { + "id": "batch_abc123", + "status": "completed", + "output_file_id": "file-xyz789" + } +} + +# Batch failed +{ + "type": "batch.failed", + "data": { + "id": "batch_abc123", + "status": "failed", + "errors": {...} + } +} + +# Batch cancelled +{ + "type": "batch.cancelled", + "data": { + "id": "batch_abc123", + "status": "cancelled" + } +} + +# Batch expired +{ + "type": "batch.expired", + "data": { + "id": "batch_abc123", + "status": "expired" + } +} +``` + +### Eval Events + +```python +# Eval run succeeded +{ + "type": "eval.run.succeeded", + "data": { + "id": "eval_run_abc123", + "status": "succeeded", + "results": {...} + } +} + +# Eval run failed +{ + "type": "eval.run.failed", + "data": { + "id": "eval_run_abc123", + "status": "failed", + "error": {...} + } +} +``` + +## Types + +```python { .api } +from typing import Union, Literal +from typing_extensions import TypeAlias + +# UnwrapWebhookEvent is a union of all possible webhook event types +UnwrapWebhookEvent: TypeAlias = Union[ + BatchCancelledWebhookEvent, + BatchCompletedWebhookEvent, + BatchExpiredWebhookEvent, + BatchFailedWebhookEvent, + EvalRunCanceledWebhookEvent, + EvalRunFailedWebhookEvent, + EvalRunSucceededWebhookEvent, + FineTuningJobCancelledWebhookEvent, + FineTuningJobFailedWebhookEvent, + FineTuningJobSucceededWebhookEvent, + RealtimeCallIncomingWebhookEvent, + ResponseCancelledWebhookEvent, + ResponseCompletedWebhookEvent, + ResponseFailedWebhookEvent, + ResponseIncompleteWebhookEvent, +] + +# Fine-tuning event types +class FineTuningJobSucceededWebhookEvent: + type: Literal["fine_tuning.job.succeeded"] + data: dict + +class FineTuningJobFailedWebhookEvent: + type: Literal["fine_tuning.job.failed"] + data: dict + +class FineTuningJobCancelledWebhookEvent: + type: Literal["fine_tuning.job.cancelled"] + data: dict + +# Batch event types +class BatchCompletedWebhookEvent: + type: Literal["batch.completed"] + data: dict + +class BatchFailedWebhookEvent: + type: Literal["batch.failed"] + data: dict + +class BatchCancelledWebhookEvent: + type: Literal["batch.cancelled"] + data: dict + +class BatchExpiredWebhookEvent: + type: Literal["batch.expired"] + data: dict + +# Eval event types +class EvalRunSucceededWebhookEvent: + type: Literal["eval.run.succeeded"] + data: dict + +class EvalRunFailedWebhookEvent: + type: Literal["eval.run.failed"] + data: dict + +class EvalRunCanceledWebhookEvent: + type: Literal["eval.run.canceled"] + data: dict + +# Response event types +class ResponseCompletedWebhookEvent: + type: Literal["response.completed"] + data: dict + +class ResponseFailedWebhookEvent: + type: Literal["response.failed"] + data: dict + +class ResponseCancelledWebhookEvent: + type: Literal["response.cancelled"] + data: dict + +class ResponseIncompleteWebhookEvent: + type: Literal["response.incomplete"] + data: dict + +# Realtime event types +class RealtimeCallIncomingWebhookEvent: + type: Literal["realtime.call.incoming"] + data: dict +``` + +## Complete Webhook Handler + +```python +from openai import OpenAI, InvalidWebhookSignatureError +from flask import Flask, request, jsonify + +app = Flask(__name__) +client = OpenAI() + +WEBHOOK_SECRET = "your-webhook-secret" + +@app.route("/webhooks/openai", methods=["POST"]) +def handle_webhook(): + # Get headers and payload + payload = request.data + headers = request.headers + + # Verify and parse + try: + event = client.webhooks.unwrap( + payload=payload, + headers=headers, + secret=WEBHOOK_SECRET + ) + except InvalidWebhookSignatureError: + return jsonify({"error": "Invalid signature"}), 401 + + # Handle event + if event.type == "fine_tuning.job.succeeded": + job_id = event.data["id"] + model = event.data["fine_tuned_model"] + print(f"Fine-tuning succeeded: {job_id} -> {model}") + + # Deploy model or notify user + deploy_model(model) + + elif event.type == "batch.completed": + batch_id = event.data["id"] + output_file = event.data["output_file_id"] + print(f"Batch completed: {batch_id}") + + # Process results + process_batch_results(batch_id, output_file) + + elif event.type == "fine_tuning.job.failed": + job_id = event.data["id"] + error = event.data.get("error") + print(f"Fine-tuning failed: {job_id}, Error: {error}") + + # Notify user of failure + notify_failure(job_id, error) + + return jsonify({"status": "received"}), 200 + +if __name__ == "__main__": + app.run(port=8080) +``` + +## Best Practices + +```python +from openai import OpenAI, InvalidWebhookSignatureError +import hmac +import hashlib + +client = OpenAI() + +# 1. Always verify signatures +def is_valid_webhook(payload: bytes, headers: dict, secret: str) -> bool: + try: + client.webhooks.verify_signature(payload, headers, secret=secret) + return True + except InvalidWebhookSignatureError: + return False + +# 2. Handle replay attacks with timestamp +def is_recent_webhook(timestamp: str, max_age_seconds: int = 300) -> bool: + import time + + event_time = int(timestamp) + current_time = int(time.time()) + + return (current_time - event_time) < max_age_seconds + +# 3. Process events idempotently +processed_events = set() + +def process_webhook_event(event_id: str, event_data: dict): + if event_id in processed_events: + print(f"Duplicate event: {event_id}") + return + + # Process event + handle_event(event_data) + + # Mark as processed + processed_events.add(event_id) + +# 4. Return 200 quickly, process async +from threading import Thread + +def handle_webhook_async(event): + # Process in background + thread = Thread(target=process_event, args=(event,)) + thread.start() + + # Return immediately + return {"status": "accepted"}, 200 + +# 5. Retry on failure +import time + +def process_with_retry(event, max_retries=3): + for attempt in range(max_retries): + try: + process_event(event) + return + except Exception as e: + if attempt == max_retries - 1: + log_failure(event, e) + raise + time.sleep(2 ** attempt) +``` + +## Testing Webhooks + +```python +# Generate test signature for development +import hmac +import hashlib +import json +import time + +def generate_test_signature(payload: dict, secret: str) -> tuple[str, str]: + """Generate signature for testing.""" + timestamp = str(int(time.time())) + payload_str = json.dumps(payload) + + # Create signature + message = f"{timestamp}.{payload_str}" + signature = hmac.new( + secret.encode(), + message.encode(), + hashlib.sha256 + ).hexdigest() + + return signature, timestamp + +# Test webhook handler +test_payload = { + "type": "fine_tuning.job.succeeded", + "data": { + "id": "ftjob-test123", + "fine_tuned_model": "ft:gpt-3.5-turbo:test" + } +} + +# Create test headers (actual implementation would vary) +test_headers = { + "webhook-signature": "v1,test_signature", + "webhook-timestamp": str(int(time.time())), + "webhook-id": "test_webhook_id" +} + +# Note: In production, signatures are generated by OpenAI +# This is a simplified example for testing +event = client.webhooks.unwrap( + payload=json.dumps(test_payload), + headers=test_headers, + secret=WEBHOOK_SECRET +) + +print(f"Test event: {event.type}") +``` + +## Security Considerations + +1. **Always verify signatures** - Never trust webhook data without verification +2. **Use HTTPS** - Only accept webhooks over HTTPS +3. **Check timestamps** - Reject old events to prevent replay attacks +4. **Rate limit** - Limit webhook endpoint to prevent abuse +5. **Store secrets securely** - Use environment variables or secret management +6. **Log failures** - Track verification failures for security monitoring diff --git a/.tessl/tiles/tessl/pypi-openai/tile.json b/.tessl/tiles/tessl/pypi-openai/tile.json new file mode 100644 index 000000000..e16c0fa0c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-openai/tile.json @@ -0,0 +1,8 @@ +{ + "name": "tessl/pypi-openai", + "version": "2.11.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/openai@2.11.0", + "summary": "The official Python library for the openai API", + "private": false +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md new file mode 100644 index 000000000..ff4beb577 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md @@ -0,0 +1,27 @@ +# Administration & User Management + +Administrative functions for managing users, workspaces, invitations, and organizational settings. + +## Capabilities + +```python { .api } +class Admin: + users: Users + invites: Invites + workspaces: Workspaces + +class Users: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + +class Workspaces: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + users: WorkspacesUsers +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md new file mode 100644 index 000000000..931774627 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md @@ -0,0 +1,71 @@ +# Assistants & Threads + +OpenAI Assistants API implementation with thread management, message handling, and tool execution support. + +## Capabilities + +### Assistant Management + +```python { .api } +class Assistants: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + +class Threads: + def create(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + messages: Messages + runs: Runs + +class Messages: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + +class Runs: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def cancel(self, **kwargs): ... + steps: Steps +``` + +## Usage Examples + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Create assistant +assistant = portkey.beta.assistants.create( + name="Math Tutor", + instructions="You are a helpful math tutor", + model="gpt-4" +) + +# Create thread +thread = portkey.beta.threads.create() + +# Add message +portkey.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="Solve 2x + 3 = 7" +) + +# Create run +run = portkey.beta.threads.runs.create( + thread_id=thread.id, + assistant_id=assistant.id +) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md new file mode 100644 index 000000000..1c3b1ab19 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md @@ -0,0 +1,27 @@ +# Batch Processing + +Batch API for processing large volumes of requests efficiently with cost optimization and result management. + +## Capabilities + +```python { .api } +class Batches: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def cancel(self, **kwargs): ... +``` + +## Usage Examples + +```python +# Create batch job +batch = portkey.batches.create( + input_file_id="file-123", + endpoint="/v1/chat/completions", + completion_window="24h" +) + +# Check batch status +batch_status = portkey.batches.retrieve(batch.id) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md new file mode 100644 index 000000000..95369bef1 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md @@ -0,0 +1,290 @@ +# Beta Realtime API + +Real-time audio and WebSocket-based AI interactions for building conversational applications with low-latency voice communication. Supports real-time session management and WebSocket connections for streaming audio communication. + +## Capabilities + +### Real-Time Connection Management + +Establishes WebSocket connections for real-time communication with AI models, enabling low-latency voice and audio interactions. + +```python { .api } +class BetaRealtime: + def connect( + self, + *, + model: str, + websocket_connection_options: WebsocketConnectionOptions = {}, + **kwargs + ) -> RealtimeConnectionManager: + """ + Create a real-time WebSocket connection to an AI model. + + Args: + model: Model identifier for real-time communication + websocket_connection_options: WebSocket configuration options + **kwargs: Additional connection parameters + + Returns: + RealtimeConnectionManager: Connection manager for real-time communication + """ + + sessions: BetaSessions + +class AsyncBetaRealtime: + def connect( + self, + *, + model: str, + websocket_connection_options: WebsocketConnectionOptions = {}, + **kwargs + ) -> AsyncRealtimeConnectionManager: + """Async version of connect method.""" + + sessions: AsyncBetaSessions +``` + +### Real-Time Session Management + +Create and manage real-time sessions with configurable audio formats, voice settings, and interaction parameters. + +```python { .api } +class BetaSessions: + def create( + self, + *, + model: Any = "portkey-default", + input_audio_format: Union[Any, NotGiven] = NOT_GIVEN, + input_audio_transcription: Union[Any, NotGiven] = NOT_GIVEN, + instructions: Union[str, NotGiven] = NOT_GIVEN, + max_response_output_tokens: Union[int, Any, NotGiven] = NOT_GIVEN, + modalities: Union[List[Any], NotGiven] = NOT_GIVEN, + output_audio_format: Union[Any, NotGiven] = NOT_GIVEN, + temperature: Union[float, NotGiven] = NOT_GIVEN, + tool_choice: Union[str, NotGiven] = NOT_GIVEN, + tools: Union[Iterable[Any], NotGiven] = NOT_GIVEN, + turn_detection: Union[Any, NotGiven] = NOT_GIVEN, + voice: Union[Any, NotGiven] = NOT_GIVEN + ) -> SessionCreateResponse: + """ + Create a real-time session for voice communication. + + Args: + model: Model to use for the session + input_audio_format: Format for input audio (e.g., "pcm16", "g711_ulaw") + input_audio_transcription: Configuration for input audio transcription + instructions: System instructions for the AI assistant + max_response_output_tokens: Maximum tokens in response + modalities: Supported modalities (audio, text) + output_audio_format: Format for output audio + temperature: Response randomness (0.0 to 2.0) + tool_choice: Tool selection strategy + tools: Available tools for the assistant + turn_detection: Turn detection configuration + voice: Voice model for audio output + + Returns: + SessionCreateResponse: Session configuration and connection details + """ + +class AsyncBetaSessions: + async def create( + self, + *, + model: Any = "portkey-default", + input_audio_format: Union[Any, NotGiven] = NOT_GIVEN, + input_audio_transcription: Union[Any, NotGiven] = NOT_GIVEN, + instructions: Union[str, NotGiven] = NOT_GIVEN, + max_response_output_tokens: Union[int, Any, NotGiven] = NOT_GIVEN, + modalities: Union[List[Any], NotGiven] = NOT_GIVEN, + output_audio_format: Union[Any, NotGiven] = NOT_GIVEN, + temperature: Union[float, NotGiven] = NOT_GIVEN, + tool_choice: Union[str, NotGiven] = NOT_GIVEN, + tools: Union[Iterable[Any], NotGiven] = NOT_GIVEN, + turn_detection: Union[Any, NotGiven] = NOT_GIVEN, + voice: Union[Any, NotGiven] = NOT_GIVEN + ) -> SessionCreateResponse: + """Async version of session creation.""" +``` + +### Usage Examples + +```python +from portkey_ai import Portkey + +# Initialize client +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Create a real-time session +session = portkey.beta.realtime.sessions.create( + model="gpt-4-realtime-preview", + modalities=["text", "audio"], + instructions="You are a helpful voice assistant.", + voice="alloy", + input_audio_format="pcm16", + output_audio_format="pcm16", + turn_detection={ + "type": "server_vad", + "threshold": 0.5, + "prefix_padding_ms": 300, + "silence_duration_ms": 200 + } +) + +print(f"Session ID: {session.id}") +print(f"Model: {session.model}") + +# Establish WebSocket connection +connection = portkey.beta.realtime.connect( + model="gpt-4-realtime-preview", + websocket_connection_options={ + "timeout": 30, + "additional_headers": { + "Authorization": f"Bearer {portkey.api_key}" + } + } +) + +# Use connection for real-time communication +# Note: Actual usage would involve WebSocket event handling +with connection as conn: + # Send audio data + conn.send_audio_data(audio_bytes) + + # Handle responses + for event in conn.listen(): + if event.type == "response.audio.delta": + # Process audio response + process_audio_chunk(event.delta) + elif event.type == "response.text.delta": + # Process text response + print(event.delta, end="") +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def create_realtime_session(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + # Create session asynchronously + session = await portkey.beta.realtime.sessions.create( + model="gpt-4-realtime-preview", + modalities=["text", "audio"], + instructions="You are a voice assistant for customer support.", + voice="nova", + temperature=0.7, + max_response_output_tokens=150 + ) + + # Establish async connection + connection = portkey.beta.realtime.connect( + model="gpt-4-realtime-preview" + ) + + return session, connection + +# Run async function +session, connection = asyncio.run(create_realtime_session()) +``` + +### Advanced Configuration + +```python +# Configure detailed session parameters +session = portkey.beta.realtime.sessions.create( + model="gpt-4-realtime-preview", + modalities=["text", "audio"], + instructions=""" + You are an AI assistant for a language learning app. + Help users practice pronunciation and provide feedback. + Speak clearly and at a moderate pace. + """, + voice="shimmer", + input_audio_format="pcm16", + output_audio_format="pcm16", + input_audio_transcription={ + "model": "whisper-1" + }, + turn_detection={ + "type": "server_vad", + "threshold": 0.6, + "prefix_padding_ms": 300, + "silence_duration_ms": 500 + }, + tools=[ + { + "type": "function", + "name": "pronunciation_feedback", + "description": "Provide pronunciation feedback", + "parameters": { + "type": "object", + "properties": { + "word": {"type": "string"}, + "accuracy": {"type": "number"}, + "feedback": {"type": "string"} + } + } + } + ], + tool_choice="auto", + temperature=0.3, + max_response_output_tokens=100 +) +``` + +## Types + +```python { .api } +class SessionCreateResponse: + """Response from real-time session creation""" + id: str # Session identifier + object: str # "realtime.session" + model: str # Model used for the session + modalities: List[str] # Supported modalities + instructions: str # System instructions + voice: str # Voice model + input_audio_format: str # Input audio format + output_audio_format: str # Output audio format + input_audio_transcription: dict # Transcription settings + turn_detection: dict # Turn detection configuration + tools: List[dict] # Available tools + tool_choice: str # Tool selection strategy + temperature: float # Response temperature + max_response_output_tokens: int # Token limit + _headers: Optional[dict] # Response headers + +class RealtimeConnectionManager: + """Synchronous WebSocket connection manager""" + def send_audio_data(self, audio_bytes: bytes) -> None: ... + def listen(self) -> Iterator[RealtimeEvent]: ... + def close(self) -> None: ... + +class AsyncRealtimeConnectionManager: + """Asynchronous WebSocket connection manager""" + async def send_audio_data(self, audio_bytes: bytes) -> None: ... + async def listen(self) -> AsyncIterator[RealtimeEvent]: ... + async def close(self) -> None: ... + +class WebsocketConnectionOptions: + """WebSocket connection configuration""" + timeout: Optional[int] # Connection timeout in seconds + additional_headers: Optional[dict] # Additional headers + # Additional WebSocket-specific options + +class RealtimeEvent: + """Real-time event from WebSocket connection""" + type: str # Event type + delta: Optional[str] # Content delta for streaming + audio: Optional[bytes] # Audio data + # Additional event-specific fields +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md new file mode 100644 index 000000000..c508c25e5 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md @@ -0,0 +1,364 @@ +# Chat Completions + +OpenAI-compatible chat completion API with support for all major providers, streaming, function calling, and advanced Portkey features like fallbacks and load balancing. + +## Capabilities + +### Chat Completion Creation + +Primary method for generating chat completions with support for multi-turn conversations, function calling, and streaming responses. + +```python { .api } +class ChatCompletion: + """Synchronous chat completion API""" + completions: Completions + +class Completions: + """Chat completions endpoint""" + + def create( + self, + *, + messages: Iterable[dict], + model: Optional[str] = "portkey-default", + stream: Optional[bool] = None, + temperature: Optional[float] = None, + max_tokens: Optional[int] = None, + top_p: Optional[float] = None, + audio: Optional[Any] = None, + max_completion_tokens: Optional[int] = None, + metadata: Optional[Dict[str, str]] = None, + modalities: Optional[List[Any]] = None, + prediction: Optional[Any] = None, + reasoning_effort: Optional[Any] = None, + store: Optional[bool] = None, + **kwargs + ) -> Union[ChatCompletions, Iterator[ChatCompletionChunk]]: + """ + Create a chat completion. + + Parameters: + - messages: List of message objects with 'role' and 'content' + - model: Model identifier (defaults to 'portkey-default') + - max_tokens: Maximum tokens to generate + - temperature: Sampling temperature + - stream: Enable streaming responses + - top_p: Nucleus sampling parameter + - audio: Audio configuration for multimodal models + - max_completion_tokens: Maximum completion tokens + - metadata: Custom metadata for request + - modalities: Supported modalities (text, audio, etc.) + - prediction: Prediction configuration + - reasoning_effort: Reasoning effort level + - store: Whether to store the conversation + + Returns: + ChatCompletions object or Iterator of ChatCompletionChunk for streaming + """ + + messages: ChatCompletionsMessages + +class AsyncChatCompletion: + """Asynchronous chat completion API""" + completions: AsyncCompletions + +class AsyncCompletions: + """Async chat completions endpoint""" + + async def create( + self, + *, + messages: Iterable[dict], + model: Optional[str] = "portkey-default", + **kwargs + ) -> Union[ChatCompletions, AsyncIterator[ChatCompletionChunk]]: + """Async version of chat completion creation""" +``` + +### Message Management + +Handle multi-turn conversations with proper message formatting and conversation context management. + +```python { .api } +class ChatCompletionsMessages: + """Message handling utilities for chat completions""" + + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + +class AsyncChatCompletionsMessages: + """Async message handling utilities""" + + async def create(self, **kwargs): ... + async def list(self, **kwargs): ... + async def retrieve(self, **kwargs): ... + async def update(self, **kwargs): ... + async def delete(self, **kwargs): ... +``` + +## Usage Examples + +### Basic Chat Completion + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Simple chat completion +response = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "What is machine learning?"} + ], + model="gpt-4" +) + +print(response.choices[0].message.content) +``` + +### Multi-turn Conversation + +```python +# Multi-turn conversation +messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Explain quantum computing"}, + {"role": "assistant", "content": "Quantum computing is..."}, + {"role": "user", "content": "What are its main applications?"} +] + +response = portkey.chat.completions.create( + messages=messages, + model="gpt-4", + max_tokens=500, + temperature=0.7 +) + +print(response.choices[0].message.content) +``` + +### Streaming Response + +```python +# Streaming chat completion +stream = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "Write a story about AI"} + ], + model="gpt-4", + stream=True +) + +for chunk in stream: + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") +``` + +### Function Calling + +```python +# Function calling with tools +tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather information for a location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "City name" + }, + "unit": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + "description": "Temperature unit" + } + }, + "required": ["location"] + } + } + } +] + +response = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "What's the weather in San Francisco?"} + ], + model="gpt-4", + tools=tools, + tool_choice="auto" +) + +# Handle function call +if response.choices[0].message.tool_calls: + tool_call = response.choices[0].message.tool_calls[0] + function_name = tool_call.function.name + function_args = tool_call.function.arguments + print(f"Function called: {function_name} with args: {function_args}") +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def chat_example(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + response = await portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "Hello, how are you?"} + ], + model="gpt-4" + ) + + print(response.choices[0].message.content) + + # Async streaming + async for chunk in await portkey.chat.completions.create( + messages=[{"role": "user", "content": "Count to 10"}], + model="gpt-4", + stream=True + ): + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") + +asyncio.run(chat_example()) +``` + +### Response Format Control + +```python +# JSON response format +response = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "List 3 colors in JSON format"} + ], + model="gpt-4", + response_format={"type": "json_object"} +) + +# Structured output with seed for reproducibility +response = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "Generate a random number"} + ], + model="gpt-4", + seed=12345, + temperature=0 +) +``` + +## Message Format + +### Message Structure + +```python +# Message object structure +message = { + "role": "user" | "assistant" | "system" | "tool", + "content": str | List[dict], # Text or multimodal content + "name": str, # Optional message name + "tool_calls": List[dict], # Tool calls (assistant messages) + "tool_call_id": str # Tool call ID (tool messages) +} +``` + +### Multimodal Content + +```python +# Text and image message +message = { + "role": "user", + "content": [ + { + "type": "text", + "text": "What's in this image?" + }, + { + "type": "image_url", + "image_url": { + "url": "https://example.com/image.jpg", + "detail": "high" + } + } + ] +} +``` + +## Advanced Features + +### Provider-Specific Parameters + +Different providers support various additional parameters: + +```python +# OpenAI-specific parameters +response = portkey.chat.completions.create( + messages=messages, + model="gpt-4", + logprobs=True, + top_logprobs=3, + logit_bias={50256: -100} # Reduce likelihood of specific tokens +) + +# Anthropic-specific parameters +response = portkey.chat.completions.create( + messages=messages, + model="claude-3-sonnet-20240229", + max_tokens=1000, + anthropic_version="2023-06-01" +) +``` + +### Portkey-Specific Features + +```python +# Multiple providers with fallback +portkey = Portkey( + api_key="PORTKEY_API_KEY", + config={ + "strategy": {"mode": "fallback"}, + "targets": [ + {"provider": "openai", "api_key": "OPENAI_KEY"}, + {"provider": "anthropic", "api_key": "ANTHROPIC_KEY"} + ] + } +) + +# Load balancing across providers +portkey = Portkey( + api_key="PORTKEY_API_KEY", + config={ + "strategy": {"mode": "loadbalance"}, + "targets": [ + {"provider": "openai", "api_key": "OPENAI_KEY", "weight": 0.7}, + {"provider": "anthropic", "api_key": "ANTHROPIC_KEY", "weight": 0.3} + ] + } +) + +# Request with custom metadata +response = portkey.chat.completions.create( + messages=messages, + model="gpt-4", + metadata={ + "user_id": "user123", + "session_id": "session456", + "environment": "production" + } +) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md new file mode 100644 index 000000000..c193eb815 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md @@ -0,0 +1,27 @@ +# Configuration Management + +Advanced configuration system for managing provider settings, routing rules, fallback strategies, and load balancing configurations. + +## Capabilities + +```python { .api } +class Configs: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` + +## Usage Examples + +```python +# Create configuration +config = portkey.configs.create({ + "strategy": {"mode": "fallback"}, + "targets": [ + {"provider": "openai", "api_key": "key1"}, + {"provider": "anthropic", "api_key": "key2"} + ] +}) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md new file mode 100644 index 000000000..8afcc90f6 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md @@ -0,0 +1,20 @@ +# Container & Content Management + +Container-based deployment and content management capabilities for scalable AI applications. + +## Capabilities + +```python { .api } +class Containers: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + files: ContainersFiles + +class Content: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md new file mode 100644 index 000000000..40a6745ba --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md @@ -0,0 +1,274 @@ +# Core Client & Configuration + +Primary client classes and configuration utilities for initializing and managing Portkey connections with comprehensive provider support and advanced routing capabilities. + +## Capabilities + +### Main Client Classes + +Core synchronous and asynchronous client classes that provide the primary interface to Portkey's AI gateway and observability features. + +```python { .api } +class Portkey: + """ + Primary synchronous client for Portkey AI API. + + Provides unified interface to 40+ AI providers with built-in fallbacks, + load balancing, caching, and comprehensive observability. + """ + def __init__( + self, + *, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + virtual_key: Optional[str] = None, + websocket_base_url: Optional[Union[str, httpx.URL]] = None, + config: Optional[Union[Mapping, str]] = None, + provider: Optional[str] = None, + trace_id: Optional[str] = None, + metadata: Union[Optional[dict[str, str]], str] = None, + cache_namespace: Optional[str] = None, + debug: Optional[bool] = None, + cache_force_refresh: Optional[bool] = None, + custom_host: Optional[str] = None, + forward_headers: Optional[List[str]] = None, + instrumentation: Optional[bool] = None, + openai_project: Optional[str] = None, + openai_organization: Optional[str] = None, + aws_secret_access_key: Optional[str] = None, + aws_access_key_id: Optional[str] = None, + aws_session_token: Optional[str] = None, + aws_region: Optional[str] = None, + vertex_project_id: Optional[str] = None, + vertex_region: Optional[str] = None, + workers_ai_account_id: Optional[str] = None, + azure_resource_name: Optional[str] = None, + azure_deployment_id: Optional[str] = None, + azure_api_version: Optional[str] = None, + azure_endpoint_name: Optional[str] = None, + huggingface_base_url: Optional[str] = None, + http_client: Optional[httpx.Client] = None, + request_timeout: Optional[int] = None, + strict_open_ai_compliance: Optional[bool] = False, + anthropic_beta: Optional[str] = None, + anthropic_version: Optional[str] = None, + mistral_fim_completion: Optional[str] = None, + vertex_storage_bucket_name: Optional[str] = None, + provider_file_name: Optional[str] = None, + provider_model: Optional[str] = None, + aws_s3_bucket: Optional[str] = None, + aws_s3_object_key: Optional[str] = None, + aws_bedrock_model: Optional[str] = None, + fireworks_account_id: Optional[str] = None, + calculate_audio_duration: Optional[bool] = True, + **kwargs + ) -> None: ... + +class AsyncPortkey: + """ + Asynchronous client for Portkey AI API. + + Async version of Portkey client with identical API surface + supporting concurrent operations and async/await patterns. + """ + def __init__(self, **kwargs) -> None: ... +``` + +### Usage Examples + +```python +# Basic initialization +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Advanced configuration with provider-specific settings +portkey = Portkey( + api_key="PORTKEY_API_KEY", + config={ + "strategy": { + "mode": "fallback" + }, + "targets": [ + { + "provider": "openai", + "api_key": "OPENAI_API_KEY" + }, + { + "provider": "anthropic", + "api_key": "ANTHROPIC_API_KEY" + } + ] + }, + metadata={ + "environment": "production", + "user_id": "user123" + }, + trace_id="trace-abc-123", + debug=True +) + +# Async client usage +import asyncio +from portkey_ai import AsyncPortkey + +async def main(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + response = await portkey.chat.completions.create( + messages=[{"role": "user", "content": "Hello"}], + model="gpt-4" + ) + + print(response) + +asyncio.run(main()) +``` + +### Header Creation Utilities + +Utility functions for creating and managing Portkey-specific request headers. + +```python { .api } +def createHeaders( + api_key: Optional[str] = None, + provider: Optional[str] = None, + trace_id: Optional[str] = None, + metadata: Optional[dict] = None, + config: Optional[Union[str, dict]] = None, + cache_namespace: Optional[str] = None, + cache_force_refresh: Optional[bool] = None, + virtual_key: Optional[str] = None, + custom_host: Optional[str] = None, + forward_headers: Optional[List[str]] = None, + openai_project: Optional[str] = None, + openai_organization: Optional[str] = None, + aws_secret_access_key: Optional[str] = None, + aws_access_key_id: Optional[str] = None, + aws_session_token: Optional[str] = None, + aws_region: Optional[str] = None, + vertex_project_id: Optional[str] = None, + vertex_region: Optional[str] = None, + workers_ai_account_id: Optional[str] = None, + azure_resource_name: Optional[str] = None, + azure_deployment_id: Optional[str] = None, + azure_api_version: Optional[str] = None, + azure_endpoint_name: Optional[str] = None, + anthropic_beta: Optional[str] = None, + anthropic_version: Optional[str] = None, + **kwargs +) -> dict: + """ + Create Portkey-specific headers for API requests. + + Returns: + Dictionary of headers for HTTP requests + """ +``` + +### Global Configuration Variables + +Module-level configuration variables for default settings. + +```python { .api } +api_key: Optional[str] +"""Global API key from environment variable PORTKEY_API_KEY""" + +base_url: Optional[str] +"""Global base URL from environment variable PORTKEY_PROXY_ENV or default""" + +config: Optional[Union[Mapping, str]] +"""Global configuration object""" + +mode: Optional[Union[Modes, ModesLiteral]] +"""Global mode setting""" +``` + +### Constants + +```python { .api } +PORTKEY_BASE_URL: str +"""Default Portkey API base URL""" + +PORTKEY_API_KEY_ENV: str +"""Environment variable name for API key""" + +PORTKEY_PROXY_ENV: str +"""Environment variable name for proxy URL""" + +PORTKEY_GATEWAY_URL: str +"""Gateway URL constant""" +``` + +## Configuration Parameters + +### Core Parameters + +- **api_key**: Portkey API key for authentication +- **base_url**: Base URL for API requests (defaults to Portkey gateway) +- **virtual_key**: Virtual key for secure credential management +- **config**: Configuration object for routing, fallbacks, and provider settings +- **provider**: Specific AI provider to use +- **trace_id**: Request tracing identifier for observability +- **metadata**: Custom metadata for request categorization and analytics + +### Caching Parameters + +- **cache_namespace**: Namespace for cache isolation +- **cache_force_refresh**: Force cache refresh for requests + +### Observability Parameters + +- **debug**: Enable debug mode for detailed logging +- **instrumentation**: Enable request instrumentation +- **forward_headers**: List of headers to forward to providers + +### Provider-Specific Parameters + +#### OpenAI +- **openai_project**: OpenAI project ID +- **openai_organization**: OpenAI organization ID + +#### AWS/Bedrock +- **aws_secret_access_key**: AWS secret access key +- **aws_access_key_id**: AWS access key ID +- **aws_session_token**: AWS session token +- **aws_region**: AWS region +- **aws_s3_bucket**: S3 bucket for file operations +- **aws_s3_object_key**: S3 object key +- **aws_bedrock_model**: Bedrock model identifier + +#### Google Vertex AI +- **vertex_project_id**: Vertex AI project ID +- **vertex_region**: Vertex AI region +- **vertex_storage_bucket_name**: Storage bucket name + +#### Azure OpenAI +- **azure_resource_name**: Azure resource name +- **azure_deployment_id**: Azure deployment ID +- **azure_api_version**: Azure API version +- **azure_endpoint_name**: Azure endpoint name + +#### Anthropic +- **anthropic_beta**: Anthropic beta features +- **anthropic_version**: Anthropic API version + +#### Other Providers +- **workers_ai_account_id**: Cloudflare Workers AI account ID +- **huggingface_base_url**: HuggingFace API base URL +- **fireworks_account_id**: Fireworks AI account ID +- **mistral_fim_completion**: Mistral fill-in-middle completion setting + +### HTTP Configuration + +- **http_client**: Custom HTTP client instance +- **request_timeout**: Request timeout in seconds +- **websocket_base_url**: WebSocket base URL for real-time features +- **custom_host**: Custom host override +- **strict_open_ai_compliance**: Enforce strict OpenAI API compliance \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md new file mode 100644 index 000000000..e2a22c12c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md @@ -0,0 +1,140 @@ +# Embeddings + +Text embedding generation for converting text into numerical vectors for semantic similarity, search, and machine learning applications. Supports multiple providers through Portkey's unified interface. + +## Capabilities + +### Embedding Creation + +Creates vector embeddings from input text using the specified model. Supports various embedding models from different providers with consistent interface. + +```python { .api } +class Embeddings: + def create( + self, + *, + input: str, + model: Optional[str] = "portkey-default", + dimensions: Union[int, NotGiven] = NOT_GIVEN, + encoding_format: Union[str, NotGiven] = NOT_GIVEN, + user: Union[str, NotGiven] = NOT_GIVEN, + **kwargs + ) -> CreateEmbeddingResponse: + """ + Create embeddings for the given input text. + + Args: + input: Input text to create embeddings for + model: Embedding model to use (defaults to "portkey-default") + dimensions: Number of dimensions for the embedding vectors (model-dependent) + encoding_format: Format for encoding the embeddings (e.g., "float", "base64") + user: User identifier for tracking and analytics + **kwargs: Additional provider-specific parameters + + Returns: + CreateEmbeddingResponse: Response containing embedding vectors and metadata + """ + +class AsyncEmbeddings: + async def create( + self, + *, + input: str, + model: Optional[str] = "portkey-default", + dimensions: Union[int, NotGiven] = NOT_GIVEN, + encoding_format: Union[str, NotGiven] = NOT_GIVEN, + user: Union[str, NotGiven] = NOT_GIVEN, + **kwargs + ) -> CreateEmbeddingResponse: + """Async version of create method.""" +``` + +### Usage Examples + +```python +from portkey_ai import Portkey + +# Initialize client +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Create embeddings for single text +response = portkey.embeddings.create( + input="Hello, world!", + model="text-embedding-ada-002" +) + +# Access the embedding vector +embedding = response.data[0].embedding +print(f"Embedding dimensions: {len(embedding)}") + +# Create embeddings with specific dimensions +response = portkey.embeddings.create( + input="Text to embed", + model="text-embedding-3-small", + dimensions=512 +) + +# Batch embedding creation (provider-dependent) +texts = [ + "First text to embed", + "Second text to embed", + "Third text to embed" +] + +for text in texts: + response = portkey.embeddings.create( + input=text, + model="text-embedding-ada-002", + user="user-123" + ) + print(f"Embedding for '{text}': {len(response.data[0].embedding)} dimensions") +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def create_embeddings(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + response = await portkey.embeddings.create( + input="Async embedding creation", + model="text-embedding-ada-002" + ) + + return response.data[0].embedding + +# Run async function +embedding = asyncio.run(create_embeddings()) +``` + +## Types + +```python { .api } +class CreateEmbeddingResponse: + """Response from embedding creation request""" + object: str # "list" + data: List[EmbeddingObject] + model: str + usage: EmbeddingUsage + _headers: Optional[dict] # Response headers for debugging + +class EmbeddingObject: + """Individual embedding object""" + object: str # "embedding" + embedding: List[float] # The embedding vector + index: int # Index in the input list + +class EmbeddingUsage: + """Token usage information""" + prompt_tokens: int + total_tokens: int +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md new file mode 100644 index 000000000..fce371c03 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md @@ -0,0 +1,22 @@ +# Evaluation & Testing + +AI evaluation framework with automated grading, test execution, and performance measurement capabilities. + +## Capabilities + +```python { .api } +class Evals: + runs: EvalsRuns + +class EvalsRuns: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Alpha: + graders: Graders + +class Graders: + def create(self, **kwargs): ... + def list(self, **kwargs): ... +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md new file mode 100644 index 000000000..b7fe03168 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md @@ -0,0 +1,19 @@ +# Feedback & Collections + +User feedback collection and data organization capabilities for improving AI applications and gathering insights. + +## Capabilities + +```python { .api } +class Feedback: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Collections: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md new file mode 100644 index 000000000..57b6af64a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md @@ -0,0 +1,45 @@ +# File Management + +File upload, management, and processing capabilities including support for assistants, fine-tuning, and batch operations. + +## Capabilities + +### File Operations + +```python { .api } +class MainFiles: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def delete(self, **kwargs): ... + +class Uploads: + def create(self, **kwargs): ... + parts: Parts + +class Parts: + def create(self, **kwargs): ... +``` + +## Usage Examples + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Upload file +file_response = portkey.files.create( + file=open("document.pdf", "rb"), + purpose="assistants" +) + +# List files +files = portkey.files.list() + +# Delete file +portkey.files.delete(file_response.id) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md new file mode 100644 index 000000000..70cf2110d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md @@ -0,0 +1,34 @@ +# Fine-Tuning + +Model fine-tuning capabilities with job management, checkpoint handling, and training monitoring. + +## Capabilities + +```python { .api } +class FineTuning: + jobs: Jobs + checkpoints: Checkpoints + +class Jobs: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def cancel(self, **kwargs): ... + +class Checkpoints: + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... +``` + +## Usage Examples + +```python +# Create fine-tuning job +job = portkey.fine_tuning.jobs.create( + training_file="file-123", + model="gpt-3.5-turbo" +) + +# Monitor job +job_status = portkey.fine_tuning.jobs.retrieve(job.id) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md new file mode 100644 index 000000000..1a2e7ebbd --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md @@ -0,0 +1,60 @@ +# Framework Integrations + +Pre-built integrations with popular AI frameworks including LangChain and LlamaIndex callback handlers. + +## Capabilities + +### LangChain Integration + +```python { .api } +from portkey_ai.langchain import PortkeyLangchainCallbackHandler + +class PortkeyLangchainCallbackHandler: + """Callback handler for LangChain integration with Portkey observability""" + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + **kwargs + ): ... +``` + +### LlamaIndex Integration + +```python { .api } +from portkey_ai.llamaindex import PortkeyLlamaCallbackHandler + +class PortkeyLlamaCallbackHandler: + """Callback handler for LlamaIndex integration with Portkey observability""" + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + **kwargs + ): ... +``` + +## Usage Examples + +```python +# LangChain integration +from langchain.llms import OpenAI +from portkey_ai.langchain import PortkeyLangchainCallbackHandler + +handler = PortkeyLangchainCallbackHandler( + api_key="PORTKEY_API_KEY" +) + +llm = OpenAI(callbacks=[handler]) +response = llm("What is AI?") + +# LlamaIndex integration +from llama_index.core import SimpleDirectoryReader +from portkey_ai.llamaindex import PortkeyLlamaCallbackHandler + +handler = PortkeyLlamaCallbackHandler( + api_key="PORTKEY_API_KEY" +) + +# Use with LlamaIndex operations +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/index.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/index.md new file mode 100644 index 000000000..41f6bf36a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/index.md @@ -0,0 +1,714 @@ +# Portkey AI + +Python client library for the Portkey API - Control Panel for AI Apps. Built on top of the OpenAI SDK, allowing seamless integration of Portkey's advanced features including AI Gateway capabilities (unified API signature, automated fallbacks & retries, load balancing, semantic caching, virtual keys), and comprehensive observability features (logging, request tracing, custom metadata, feedback collection, analytics). + +## Package Information + +- **Package Name**: portkey-ai +- **Language**: Python +- **Installation**: `pip install portkey-ai` + +## Core Imports + +```python +from portkey_ai import Portkey, AsyncPortkey +``` + +For OpenAI compatibility: + +```python +from portkey_ai import openai +``` + +For specific components: + +```python +from portkey_ai import ( + Config, + LLMOptions, + Modes, + ProviderTypes, + createHeaders +) +``` + +## Basic Usage + +```python +from portkey_ai import Portkey + +# Initialize client with API key and virtual key +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Make a chat completion request +chat_completion = portkey.chat.completions.create( + messages=[{"role": "user", "content": "Say this is a test"}], + model="gpt-4" +) + +print(chat_completion) +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def main(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + chat_completion = await portkey.chat.completions.create( + messages=[{"role": "user", "content": "Say this is a test"}], + model="gpt-4" + ) + + print(chat_completion) + +asyncio.run(main()) +``` + +## Architecture + +Portkey's architecture centers around two main client classes that provide comprehensive AI API management: + +- **Portkey/AsyncPortkey**: Main client classes providing unified interface to 40+ AI providers +- **API Resources**: Organized into functional areas (chat, completions, images, audio, etc.) +- **Configuration System**: Flexible config management for providers, fallbacks, retries, and routing +- **Observability Layer**: Built-in logging, tracing, analytics, and feedback collection +- **Security Layer**: Virtual key management and secure credential handling + +The dual client pattern ensures both synchronous and asynchronous support across all operations, while maintaining full OpenAI SDK compatibility. + +## Capabilities + +### Core Client & Configuration + +Primary client classes and configuration utilities for initializing and managing Portkey connections with comprehensive provider support and advanced routing capabilities. + +```python { .api } +class Portkey: + def __init__( + self, + *, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + virtual_key: Optional[str] = None, + config: Optional[Union[Mapping, str]] = None, + provider: Optional[str] = None, + trace_id: Optional[str] = None, + metadata: Union[Optional[dict[str, str]], str] = None, + **kwargs + ) -> None: ... + +class AsyncPortkey: + def __init__(self, **kwargs) -> None: ... + +def createHeaders(**kwargs) -> dict: ... +``` + +[Core Client & Configuration](./core-client.md) + +### Chat Completions + +OpenAI-compatible chat completion API with support for all major providers, streaming, function calling, and advanced Portkey features like fallbacks and load balancing. + +```python { .api } +class ChatCompletion: + completions: Completions + +class Completions: + def create( + self, + *, + messages: Iterable[dict], + model: Optional[str] = "portkey-default", + **kwargs + ) -> Union[ChatCompletions, Iterator[ChatCompletionChunk]]: ... + + messages: ChatCompletionsMessages + +class AsyncChatCompletion: + completions: AsyncCompletions +``` + +[Chat Completions](./chat-completions.md) + +### Text Completions + +Legacy text completion API for non-chat models, supporting streaming and all OpenAI-compatible parameters with Portkey enhancements. + +```python { .api } +class Completion: + def create( + self, + *, + prompt: Union[str, List[str]], + model: str, + **kwargs + ) -> Union[TextCompletion, Iterator[TextCompletionChunk]]: ... + +class AsyncCompletion: + async def create( + self, + *, + prompt: Union[str, List[str]], + model: str, + **kwargs + ) -> Union[TextCompletion, AsyncIterator[TextCompletionChunk]]: ... +``` + +[Text Completions](./text-completions.md) + +### Prompt Management + +Advanced prompt management system with templating, rendering, versioning, and prompt execution capabilities. + +```python { .api } +class Prompts: + def render(self, **kwargs): ... + def completions(self, **kwargs): ... + +class AsyncPrompts: + async def render(self, **kwargs): ... + async def completions(self, **kwargs): ... + +class Generations: + def create(self, **kwargs): ... + +class AsyncGenerations: + async def create(self, **kwargs): ... +``` + +[Prompt Management](./prompt-management.md) + +### Embeddings + +Text embedding generation for semantic similarity, search, and machine learning applications with support for multiple providers. + +```python { .api } +class Embeddings: + def create( + self, + *, + input: str, + model: Optional[str] = "portkey-default", + dimensions: Union[int, NotGiven] = NOT_GIVEN, + encoding_format: Union[str, NotGiven] = NOT_GIVEN, + user: Union[str, NotGiven] = NOT_GIVEN, + **kwargs + ) -> CreateEmbeddingResponse: ... + +class AsyncEmbeddings: + async def create(self, **kwargs) -> CreateEmbeddingResponse: ... +``` + +[Embeddings](./embeddings.md) + +### Models + +Model management and information retrieval for accessing available AI models across different providers. + +```python { .api } +class Models: + def list(self, **kwargs) -> ModelList: ... + def retrieve( + self, + model: str, + *, + timeout: Union[float, NotGiven] = NOT_GIVEN, + **kwargs + ) -> Model: ... + def delete( + self, + model: str, + *, + timeout: Union[float, NotGiven] = NOT_GIVEN, + **kwargs + ) -> ModelDeleted: ... + +class AsyncModels: + async def list(self, **kwargs) -> ModelList: ... + async def retrieve(self, model: str, **kwargs) -> Model: ... + async def delete(self, model: str, **kwargs) -> ModelDeleted: ... +``` + +[Models](./models.md) + +### Multimodal APIs + +Image generation, audio processing (speech-to-text, text-to-speech, translation), and content moderation capabilities with OpenAI compatibility. + +```python { .api } +class Images: + def generate(self, **kwargs): ... + def create_variation(self, **kwargs): ... + def edit(self, **kwargs): ... + +class Audio: + transcriptions: Transcriptions + translations: Translations + speech: Speech + +class Moderations: + def create(self, **kwargs): ... +``` + +[Multimodal APIs](./multimodal-apis.md) + +### File Management + +File upload, management, and processing capabilities including support for assistants, fine-tuning, and batch operations. + +```python { .api } +class MainFiles: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def delete(self, **kwargs): ... + +class Uploads: + def create(self, **kwargs): ... + parts: Parts +``` + +[File Management](./file-management.md) + +### Assistants & Threads + +OpenAI Assistants API implementation with thread management, message handling, and tool execution support. + +```python { .api } +class Assistants: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + +class Threads: + def create(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + messages: Messages + runs: Runs +``` + +[Assistants & Threads](./assistants-threads.md) + +### Vector Stores + +Vector database operations for retrieval-augmented generation (RAG) applications with file management and batch processing. + +```python { .api } +class VectorStores: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + files: VectorFiles + file_batches: VectorFileBatches +``` + +[Vector Stores](./vector-stores.md) + +### Fine-Tuning + +Model fine-tuning capabilities with job management, checkpoint handling, and training monitoring. + +```python { .api } +class FineTuning: + jobs: Jobs + checkpoints: Checkpoints + +class Jobs: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def cancel(self, **kwargs): ... +``` + +[Fine-Tuning](./fine-tuning.md) + +### Batch Processing + +Batch API for processing large volumes of requests efficiently with cost optimization and result management. + +```python { .api } +class Batches: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def cancel(self, **kwargs): ... +``` + +[Batch Processing](./batch-processing.md) + +### Administration & User Management + +Administrative functions for managing users, workspaces, invitations, and organizational settings. + +```python { .api } +class Admin: + users: Users + invites: Invites + workspaces: Workspaces + +class Users: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` + +[Administration & User Management](./administration.md) + +### Configuration Management + +Advanced configuration system for managing provider settings, routing rules, fallback strategies, and load balancing configurations. + +```python { .api } +class Configs: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` + +[Configuration Management](./configuration-management.md) + +### API Key & Virtual Key Management + +Secure API key and virtual key management for credential protection and access control. + +```python { .api } +class ApiKeys: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + +class VirtualKeys: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` + +[API Key & Virtual Key Management](./key-management.md) + +### Observability & Analytics + +Comprehensive logging, request tracing, analytics, and monitoring capabilities with custom metadata and performance metrics. + +```python { .api } +class Logs: + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Responses: + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + input_items: InputItems + output_items: OutputItems + +class Labels: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` + +[Observability & Analytics](./observability-analytics.md) + +### Feedback & Collections + +User feedback collection and data organization capabilities for improving AI applications and gathering insights. + +```python { .api } +class Feedback: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Collections: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` + +[Feedback & Collections](./feedback-collections.md) + +### Evaluation & Testing + +AI evaluation framework with automated grading, test execution, and performance measurement capabilities. + +```python { .api } +class Evals: + runs: EvalsRuns + +class EvalsRuns: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Alpha: + graders: Graders + +class Graders: + def create(self, **kwargs): ... + def list(self, **kwargs): ... +``` + +[Evaluation & Testing](./evaluation-testing.md) + +### Container & Content Management + +Container-based deployment and content management capabilities for scalable AI applications. + +```python { .api } +class Containers: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + files: ContainersFiles + +class Content: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... +``` + +[Container & Content Management](./container-content.md) + +### Large File Uploads + +Multi-part upload system for large files with chunked upload support and integrity verification. + +```python { .api } +class Uploads: + def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: Any, + **kwargs + ) -> Upload: ... + + def upload_file_chunked( + self, + *, + file: Union[os.PathLike[str], bytes], + mime_type: str, + purpose: Any, + **kwargs + ) -> Any: ... + + def complete( + self, + upload_id: str, + *, + part_ids: List[str], + **kwargs + ) -> Upload: ... + + parts: Parts + +class AsyncUploads: + async def create(self, **kwargs) -> Upload: ... + async def upload_file_chunked(self, **kwargs) -> Any: ... + async def complete(self, upload_id: str, **kwargs) -> Upload: ... + parts: AsyncParts +``` + +[Large File Uploads](./uploads.md) + +### Beta Realtime API + +Real-time audio and WebSocket-based AI interactions for building conversational applications with low-latency voice communication. + +```python { .api } +class BetaRealtime: + def connect( + self, + *, + model: str, + websocket_connection_options: WebsocketConnectionOptions = {}, + **kwargs + ) -> RealtimeConnectionManager: ... + + sessions: BetaSessions + +class BetaSessions: + def create( + self, + *, + model: Any = "portkey-default", + modalities: Union[List[Any], NotGiven] = NOT_GIVEN, + voice: Union[Any, NotGiven] = NOT_GIVEN, + instructions: Union[str, NotGiven] = NOT_GIVEN, + **kwargs + ) -> SessionCreateResponse: ... + +class AsyncBetaRealtime: + def connect(self, **kwargs) -> AsyncRealtimeConnectionManager: ... + sessions: AsyncBetaSessions +``` + +[Beta Realtime API](./beta-realtime.md) + +### Provider & Integration Management + +Management of AI providers and third-party integrations with workspace-level and model-level configuration. + +```python { .api } +class Providers: + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Integrations: + workspaces: IntegrationsWorkspaces + models: IntegrationsModels + +class IntegrationsWorkspaces: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... +``` + +[Provider & Integration Management](./provider-integration.md) + +### Framework Integrations + +Pre-built integrations with popular AI frameworks including LangChain and LlamaIndex callback handlers. + +```python { .api } +from portkey_ai.langchain import PortkeyLangchainCallbackHandler +from portkey_ai.llamaindex import PortkeyLlamaCallbackHandler +``` + +[Framework Integrations](./framework-integrations.md) + +## Types + +### Core Configuration Types + +```python { .api } +class Config(TypedDict): + """Configuration object for Portkey requests""" + ... + +class LLMOptions(TypedDict): + """LLM-specific options and parameters""" + ... + +class Modes(Enum): + """Available Portkey modes""" + SINGLE = "single" + FALLBACK = "fallback" + LOADBALANCE = "loadbalance" + AB_TEST = "ab_test" + +class ProviderTypes(Enum): + """Supported AI providers""" + OPENAI = "openai" + ANTHROPIC = "anthropic" + AZURE_OPENAI = "azure-openai" + COHERE = "cohere" + HUGGINGFACE = "huggingface" + # ... and 35+ more providers + +class RetrySettings(TypedDict): + """Retry configuration settings""" + attempts: int + on_status_codes: List[int] +``` + +### Response Types + +```python { .api } +class PortkeyResponse: + """Base response class for all Portkey operations""" + ... + +class ChatCompletions: + """Chat completion response""" + id: str + object: str + created: int + model: str + choices: List[dict] + usage: dict + +class ChatCompletionChunk: + """Streaming chat completion chunk""" + id: str + object: str + created: int + model: str + choices: List[dict] + +class TextCompletion: + """Text completion response""" + id: str + object: str + created: int + model: str + choices: List[dict] + usage: dict + +class Message: + """Message object for chat completions""" + role: str + content: str +``` + +### Exception Types + +```python { .api } +class APIStatusError(Exception): + """Base exception for API status errors""" + ... + +class BadRequestError(APIStatusError): + """400 Bad Request error""" + ... + +class AuthenticationError(APIStatusError): + """401 Authentication error""" + ... + +class PermissionDeniedError(APIStatusError): + """403 Permission denied error""" + ... + +class NotFoundError(APIStatusError): + """404 Not found error""" + ... + +class RateLimitError(APIStatusError): + """429 Rate limit error""" + ... + +class InternalServerError(APIStatusError): + """500 Internal server error""" + ... +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md new file mode 100644 index 000000000..68a826589 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md @@ -0,0 +1,21 @@ +# API Key & Virtual Key Management + +Secure API key and virtual key management for credential protection and access control. + +## Capabilities + +```python { .api } +class ApiKeys: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + +class VirtualKeys: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/models.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/models.md new file mode 100644 index 000000000..d1dc28678 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/models.md @@ -0,0 +1,206 @@ +# Models + +Model management and information retrieval for accessing available AI models across different providers. Provides unified interface to list, retrieve details, and manage models from various AI providers. + +## Capabilities + +### Model Listing + +Retrieves list of available models from the configured provider(s), including model capabilities, permissions, and availability status. + +```python { .api } +class Models: + def list(self, **kwargs) -> ModelList: + """ + List available models from the configured provider. + + Args: + **kwargs: Additional provider-specific parameters + + Returns: + ModelList: List of available models with metadata + """ + + def retrieve( + self, + model: str, + *, + timeout: Union[float, NotGiven] = NOT_GIVEN, + **kwargs + ) -> Model: + """ + Retrieve detailed information about a specific model. + + Args: + model: Model identifier (e.g., "gpt-4", "claude-3-opus") + timeout: Request timeout in seconds + **kwargs: Additional provider-specific parameters + + Returns: + Model: Detailed model information and capabilities + """ + + def delete( + self, + model: str, + *, + timeout: Union[float, NotGiven] = NOT_GIVEN, + **kwargs + ) -> ModelDeleted: + """ + Delete a custom model (if supported by provider). + + Args: + model: Model identifier to delete + timeout: Request timeout in seconds + **kwargs: Additional provider-specific parameters + + Returns: + ModelDeleted: Confirmation of model deletion + """ + +class AsyncModels: + async def list(self, **kwargs) -> ModelList: + """Async version of list method.""" + + async def retrieve( + self, + model: str, + *, + timeout: Union[float, NotGiven] = NOT_GIVEN, + **kwargs + ) -> Model: + """Async version of retrieve method.""" + + async def delete( + self, + model: str, + *, + timeout: Union[float, NotGiven] = NOT_GIVEN, + **kwargs + ) -> ModelDeleted: + """Async version of delete method.""" +``` + +### Usage Examples + +```python +from portkey_ai import Portkey + +# Initialize client +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# List all available models +models = portkey.models.list() +print(f"Available models: {len(models.data)}") + +for model in models.data: + print(f"- {model.id}: {model.owned_by}") + +# Get details for a specific model +model_info = portkey.models.retrieve("gpt-4") +print(f"Model: {model_info.id}") +print(f"Created: {model_info.created}") +print(f"Owned by: {model_info.owned_by}") +print(f"Permissions: {model_info.permission}") + +# Filter models by provider (provider-specific) +openai_models = portkey.models.list(provider="openai") +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def list_models(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + # List models asynchronously + models = await portkey.models.list() + + # Get model details + model_details = await portkey.models.retrieve("gpt-4") + + return models, model_details + +models, details = asyncio.run(list_models()) +print(f"Found {len(models.data)} models") +print(f"GPT-4 details: {details.id}") +``` + +### Multi-Provider Model Discovery + +```python +# List models across multiple providers using Portkey configuration +config = { + "mode": "fallback", + "options": [ + {"provider": "openai", "api_key": "openai_key"}, + {"provider": "anthropic", "api_key": "anthropic_key"}, + {"provider": "cohere", "api_key": "cohere_key"} + ] +} + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + config=config +) + +# This will list models from the primary provider in the fallback chain +all_models = portkey.models.list() + +# Access models by capability +chat_models = [m for m in all_models.data if "chat" in m.id.lower()] +completion_models = [m for m in all_models.data if "text" in m.id.lower()] +``` + +## Types + +```python { .api } +class ModelList: + """List of available models""" + object: str # "list" + data: List[Model] + _headers: Optional[dict] # Response headers + +class Model: + """Individual model information""" + id: str # Model identifier + object: str # "model" + created: int # Unix timestamp of creation + owned_by: str # Organization that owns the model + permission: List[ModelPermission] # Model permissions + root: Optional[str] # Root model identifier + parent: Optional[str] # Parent model identifier + _headers: Optional[dict] # Response headers + +class ModelPermission: + """Model permission details""" + id: str + object: str # "model_permission" + created: int + allow_create_engine: bool + allow_sampling: bool + allow_logprobs: bool + allow_search_indices: bool + allow_view: bool + allow_fine_tuning: bool + organization: str + group: Optional[str] + is_blocking: bool + +class ModelDeleted: + """Model deletion confirmation""" + id: str # Deleted model identifier + object: str # "model" + deleted: bool # True if successfully deleted + _headers: Optional[dict] # Response headers +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md new file mode 100644 index 000000000..902587257 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md @@ -0,0 +1,68 @@ +# Multimodal APIs + +Image generation, audio processing (speech-to-text, text-to-speech, translation), and content moderation capabilities with OpenAI compatibility. + +## Capabilities + +### Image Generation + +```python { .api } +class Images: + def generate(self, **kwargs): ... + def create_variation(self, **kwargs): ... + def edit(self, **kwargs): ... +``` + +### Audio Processing + +```python { .api } +class Audio: + transcriptions: Transcriptions + translations: Translations + speech: Speech + +class Transcriptions: + def create(self, **kwargs): ... + +class Translations: + def create(self, **kwargs): ... + +class Speech: + def create(self, **kwargs): ... +``` + +### Content Moderation + +```python { .api } +class Moderations: + def create(self, **kwargs): ... +``` + +## Usage Examples + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Generate image +image = portkey.images.generate( + prompt="A cat in a spacesuit", + model="dall-e-3", + size="1024x1024" +) + +# Transcribe audio +transcription = portkey.audio.transcriptions.create( + file=open("audio.mp3", "rb"), + model="whisper-1" +) + +# Moderate content +moderation = portkey.moderations.create( + input="This is a test message" +) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md new file mode 100644 index 000000000..a9706cd65 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md @@ -0,0 +1,528 @@ +# Observability & Analytics + +Comprehensive logging, request tracing, analytics, and monitoring capabilities with custom metadata and performance metrics. + +## Capabilities + +### Request Logging + +Track and analyze all API requests with detailed logging capabilities including request/response data, performance metrics, and custom metadata. + +```python { .api } +class Logs: + """Request logging and retrieval""" + + def list( + self, + *, + limit: Optional[int] = None, + after: Optional[str] = None, + before: Optional[str] = None, + order: Optional[str] = None, + filters: Optional[dict] = None, + **kwargs + ) -> dict: + """ + List request logs with filtering and pagination. + + Parameters: + - limit: Maximum number of logs to retrieve + - after: Cursor for pagination (logs after this ID) + - before: Cursor for pagination (logs before this ID) + - order: Sort order ('asc' or 'desc') + - filters: Filter criteria (date range, model, provider, etc.) + + Returns: + Dictionary containing log entries and pagination info + """ + + def retrieve( + self, + log_id: str, + **kwargs + ) -> dict: + """ + Retrieve a specific log entry by ID. + + Parameters: + - log_id: Unique identifier for the log entry + + Returns: + Detailed log entry with request/response data + """ + +class AsyncLogs: + """Async request logging and retrieval""" + + async def list(self, **kwargs) -> dict: ... + async def retrieve(self, log_id: str, **kwargs) -> dict: ... +``` + +### Response Analysis + +Analyze API responses with detailed breakdowns of input/output tokens, performance metrics, and content analysis. + +```python { .api } +class Responses: + """Response analysis and metrics""" + + def list( + self, + *, + limit: Optional[int] = None, + after: Optional[str] = None, + before: Optional[str] = None, + filters: Optional[dict] = None, + **kwargs + ) -> dict: + """List and analyze API responses""" + + def retrieve( + self, + response_id: str, + **kwargs + ) -> dict: + """Retrieve detailed response analysis""" + + input_items: InputItems + output_items: OutputItems + +class InputItems: + """Input content analysis""" + + def list(self, **kwargs) -> dict: ... + def retrieve(self, item_id: str, **kwargs) -> dict: ... + +class OutputItems: + """Output content analysis""" + + def list(self, **kwargs) -> dict: ... + def retrieve(self, item_id: str, **kwargs) -> dict: ... + +class AsyncResponses: + """Async response analysis""" + + async def list(self, **kwargs) -> dict: ... + async def retrieve(self, response_id: str, **kwargs) -> dict: ... + input_items: AsyncInputItems + output_items: AsyncOutputItems +``` + +### Request Labeling + +Categorize and tag API requests for better organization, analysis, and filtering. + +```python { .api } +class Labels: + """Request labeling and categorization""" + + def create( + self, + *, + log_id: str, + label: str, + value: Optional[str] = None, + **kwargs + ) -> dict: + """ + Create a label for a request log. + + Parameters: + - log_id: ID of the log entry to label + - label: Label name/category + - value: Optional label value + + Returns: + Created label object + """ + + def list( + self, + *, + log_id: Optional[str] = None, + label: Optional[str] = None, + **kwargs + ) -> dict: + """List labels with optional filtering""" + + def update( + self, + label_id: str, + *, + value: Optional[str] = None, + **kwargs + ) -> dict: + """Update an existing label""" + + def delete( + self, + label_id: str, + **kwargs + ) -> dict: + """Delete a label""" + +class AsyncLabels: + """Async request labeling""" + + async def create(self, **kwargs) -> dict: ... + async def list(self, **kwargs) -> dict: ... + async def update(self, label_id: str, **kwargs) -> dict: ... + async def delete(self, label_id: str, **kwargs) -> dict: ... +``` + +## Usage Examples + +### Basic Request Logging + +```python +from portkey_ai import Portkey + +# Initialize with observability enabled +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY", + debug=True, # Enable detailed logging + metadata={ + "environment": "production", + "user_id": "user123", + "session_id": "session456" + } +) + +# Make a request (automatically logged) +response = portkey.chat.completions.create( + messages=[{"role": "user", "content": "Hello"}], + model="gpt-4", + metadata={ + "request_type": "greeting", + "category": "customer_support" + } +) + +# Retrieve recent logs +logs = portkey.logs.list(limit=10, order="desc") +print(f"Found {len(logs['data'])} recent requests") + +for log in logs['data']: + print(f"Request {log['id']}: {log['model']} - {log['status']}") + print(f" Tokens: {log['usage']['total_tokens']}") + print(f" Duration: {log['duration_ms']}ms") +``` + +### Advanced Log Filtering + +```python +from datetime import datetime, timedelta + +# Filter logs by date range and criteria +yesterday = datetime.now() - timedelta(days=1) +logs = portkey.logs.list( + filters={ + "date": { + "gte": yesterday.isoformat(), + "lte": datetime.now().isoformat() + }, + "model": "gpt-4", + "status": "success", + "provider": "openai", + "metadata.environment": "production" + }, + limit=50 +) + +# Analyze request patterns +total_requests = len(logs['data']) +total_tokens = sum(log['usage']['total_tokens'] for log in logs['data']) +avg_latency = sum(log['duration_ms'] for log in logs['data']) / total_requests + +print(f"Analysis for last 24 hours:") +print(f" Total requests: {total_requests}") +print(f" Total tokens: {total_tokens}") +print(f" Average latency: {avg_latency:.2f}ms") +``` + +### Request Tracing + +```python +import uuid + +# Use trace IDs to track request flows +trace_id = str(uuid.uuid4()) + +# First request in trace +response1 = portkey.chat.completions.create( + messages=[{"role": "user", "content": "What is Python?"}], + model="gpt-4", + trace_id=trace_id, + metadata={"step": "initial_query"} +) + +# Follow-up request in same trace +response2 = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "What is Python?"}, + {"role": "assistant", "content": response1.choices[0].message.content}, + {"role": "user", "content": "Give me a code example"} + ], + model="gpt-4", + trace_id=trace_id, + metadata={"step": "follow_up"} +) + +# Retrieve all logs for this trace +trace_logs = portkey.logs.list( + filters={"trace_id": trace_id} +) + +print(f"Trace {trace_id} has {len(trace_logs['data'])} requests") +``` + +### Response Analysis + +```python +# Analyze response patterns and content +responses = portkey.responses.list( + filters={ + "model": "gpt-4", + "date": {"gte": "2024-01-01"} + }, + limit=100 +) + +# Analyze input/output patterns +for response in responses['data']: + response_detail = portkey.responses.retrieve(response['id']) + + print(f"Response {response['id']}:") + print(f" Input tokens: {response_detail['input_tokens']}") + print(f" Output tokens: {response_detail['output_tokens']}") + print(f" Cost: ${response_detail['cost']:.4f}") + + # Analyze input items + input_items = portkey.responses.input_items.list( + response_id=response['id'] + ) + + # Analyze output items + output_items = portkey.responses.output_items.list( + response_id=response['id'] + ) +``` + +### Request Labeling and Categorization + +```python +# Create labels for request categorization +recent_logs = portkey.logs.list(limit=20) + +for log in recent_logs['data']: + # Auto-label based on content + if 'translate' in log['request']['messages'][0]['content'].lower(): + portkey.labels.create( + log_id=log['id'], + label="category", + value="translation" + ) + elif 'code' in log['request']['messages'][0]['content'].lower(): + portkey.labels.create( + log_id=log['id'], + label="category", + value="coding" + ) + + # Label by performance + if log['duration_ms'] > 5000: + portkey.labels.create( + log_id=log['id'], + label="performance", + value="slow" + ) + +# Query logs by labels +coding_requests = portkey.logs.list( + filters={"labels.category": "coding"} +) + +slow_requests = portkey.logs.list( + filters={"labels.performance": "slow"} +) + +print(f"Found {len(coding_requests['data'])} coding requests") +print(f"Found {len(slow_requests['data'])} slow requests") +``` + +### Real-time Monitoring + +```python +import time + +def monitor_requests(): + """Monitor requests in real-time""" + last_check = datetime.now() + + while True: + # Get new logs since last check + new_logs = portkey.logs.list( + filters={ + "date": {"gte": last_check.isoformat()} + }, + order="asc" + ) + + for log in new_logs['data']: + print(f"New request: {log['id']}") + print(f" Model: {log['model']}") + print(f" Status: {log['status']}") + print(f" Duration: {log['duration_ms']}ms") + print(f" Tokens: {log['usage']['total_tokens']}") + + # Alert on errors + if log['status'] == 'error': + print(f" ERROR: {log['error']['message']}") + + # Alert on slow requests + if log['duration_ms'] > 10000: + print(f" WARNING: Slow request ({log['duration_ms']}ms)") + + last_check = datetime.now() + time.sleep(30) # Check every 30 seconds + +# Run monitoring (in production, use proper async/threading) +# monitor_requests() +``` + +### Custom Analytics Dashboard + +```python +from collections import defaultdict + +def generate_analytics_report(days=7): + """Generate comprehensive analytics report""" + + # Get logs for specified period + start_date = datetime.now() - timedelta(days=days) + logs = portkey.logs.list( + filters={ + "date": {"gte": start_date.isoformat()} + }, + limit=1000 + ) + + # Aggregate metrics + metrics = { + 'total_requests': len(logs['data']), + 'by_model': defaultdict(int), + 'by_provider': defaultdict(int), + 'by_status': defaultdict(int), + 'total_tokens': 0, + 'total_cost': 0, + 'avg_latency': 0, + 'error_rate': 0 + } + + latencies = [] + errors = 0 + + for log in logs['data']: + metrics['by_model'][log['model']] += 1 + metrics['by_provider'][log['provider']] += 1 + metrics['by_status'][log['status']] += 1 + metrics['total_tokens'] += log['usage']['total_tokens'] + metrics['total_cost'] += log['cost'] + latencies.append(log['duration_ms']) + + if log['status'] == 'error': + errors += 1 + + metrics['avg_latency'] = sum(latencies) / len(latencies) if latencies else 0 + metrics['error_rate'] = (errors / len(logs['data'])) * 100 if logs['data'] else 0 + + # Print report + print(f"Analytics Report ({days} days)") + print("=" * 40) + print(f"Total Requests: {metrics['total_requests']}") + print(f"Total Tokens: {metrics['total_tokens']:,}") + print(f"Total Cost: ${metrics['total_cost']:.2f}") + print(f"Average Latency: {metrics['avg_latency']:.0f}ms") + print(f"Error Rate: {metrics['error_rate']:.2f}%") + + print("\nTop Models:") + for model, count in sorted(metrics['by_model'].items(), key=lambda x: x[1], reverse=True)[:5]: + print(f" {model}: {count} requests") + + print("\nProviders:") + for provider, count in metrics['by_provider'].items(): + print(f" {provider}: {count} requests") + + return metrics + +# Generate report +analytics = generate_analytics_report(days=30) +``` + +### Async Observability Operations + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def async_observability_example(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + # Make async request with metadata + response = await portkey.chat.completions.create( + messages=[{"role": "user", "content": "Hello async world"}], + model="gpt-4", + metadata={"async": "true", "test": "observability"} + ) + + # Async log retrieval + logs = await portkey.logs.list(limit=5) + + # Async labeling + if logs['data']: + await portkey.labels.create( + log_id=logs['data'][0]['id'], + label="async_test", + value="true" + ) + + # Async response analysis + responses = await portkey.responses.list(limit=5) + for resp in responses['data']: + detail = await portkey.responses.retrieve(resp['id']) + print(f"Response {resp['id']}: {detail['input_tokens']} → {detail['output_tokens']} tokens") + +asyncio.run(async_observability_example()) +``` + +## Analytics Metrics + +Portkey automatically tracks 40+ production-critical metrics including: + +### Performance Metrics +- Request latency (p50, p95, p99) +- Throughput (requests per minute/hour) +- Error rates by provider/model +- Token usage and costs + +### Usage Metrics +- Requests by model, provider, user +- Token consumption patterns +- Cost analysis and optimization +- Geographic distribution + +### Quality Metrics +- Response quality scores +- User feedback integration +- A/B test results +- Conversion tracking + +### Infrastructure Metrics +- Cache hit rates +- Fallback trigger frequency +- Load balancing distribution +- Provider availability \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md new file mode 100644 index 000000000..b415c8295 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md @@ -0,0 +1,254 @@ +# Prompt Management + +Advanced prompt management system with templating, rendering, versioning, and prompt execution capabilities. + +## Capabilities + +### Prompt Rendering and Execution + +Advanced prompt management with templating, versioning, and execution capabilities. + +```python { .api } +class Prompts: + def render( + self, + *, + prompt_id: str, + variables: Mapping[str, Any] + ) -> PromptRender: + """ + Render a prompt template with variables. + + Args: + prompt_id: Unique identifier for the prompt template + variables: Dictionary of variables to substitute in template + + Returns: + PromptRender: Rendered prompt content + """ + + def completions( + self, + *, + prompt_id: str, + variables: Optional[Mapping[str, Any]] = None, + **kwargs + ) -> Union[PromptCompletion, Stream[PromptCompletionChunk]]: + """ + Execute a prompt template and generate completion. + + Args: + prompt_id: Unique identifier for the prompt template + variables: Variables for template substitution + **kwargs: Additional completion parameters (model, temperature, etc.) + + Returns: + PromptCompletion: Completion response or streaming chunks + """ + + completions: Completions + versions: PromptVersions + partials: PromptPartials + +class AsyncPrompts: + async def render( + self, + *, + prompt_id: str, + variables: Mapping[str, Any] + ) -> PromptRender: + """Async version of render method.""" + + async def completions( + self, + *, + prompt_id: str, + variables: Optional[Mapping[str, Any]] = None, + **kwargs + ) -> Union[PromptCompletion, AsyncStream[PromptCompletionChunk]]: + """Async version of completions method.""" + + completions: AsyncCompletions + versions: AsyncPromptVersions + partials: AsyncPromptPartials +``` + +### Prompt Versions + +Manage different versions of prompt templates for A/B testing and iterative improvement. + +```python { .api } +class PromptVersions: + def list(self, prompt_id: str, **kwargs): ... + def retrieve(self, prompt_id: str, version_id: str, **kwargs): ... + def create(self, prompt_id: str, **kwargs): ... + def update(self, prompt_id: str, version_id: str, **kwargs): ... + +class AsyncPromptVersions: + async def list(self, prompt_id: str, **kwargs): ... + async def retrieve(self, prompt_id: str, version_id: str, **kwargs): ... + async def create(self, prompt_id: str, **kwargs): ... + async def update(self, prompt_id: str, version_id: str, **kwargs): ... +``` + +### Prompt Partials + +Handle partial prompt rendering and composition for complex prompt structures. + +```python { .api } +class PromptPartials: + def render(self, **kwargs): ... + def create(self, **kwargs): ... + +class AsyncPromptPartials: + async def render(self, **kwargs): ... + async def create(self, **kwargs): ... +``` + +### Generation Management (Deprecated) + +Legacy prompt generation API - use Prompts API instead for new implementations. + +```python { .api } +class Generations: + def create( + self, + *, + prompt_id: str, + config: Optional[Union[Mapping, str]] = None, + variables: Optional[Mapping[str, Any]] = None + ) -> Union[GenericResponse, Stream[GenericResponse]]: + """ + DEPRECATED: Create prompt generation. + Use portkey.prompts.completions() instead. + """ + +class AsyncGenerations: + async def create( + self, + *, + prompt_id: str, + config: Optional[Union[Mapping, str]] = None, + variables: Optional[Mapping[str, Any]] = None + ) -> Union[GenericResponse, AsyncStream[GenericResponse]]: + """DEPRECATED: Async version of create method.""" +``` + +## Usage Examples + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Render prompt template +rendered = portkey.prompts.render( + prompt_id="template_123", + variables={"name": "Alice", "topic": "AI"} +) + +print(f"Rendered prompt: {rendered.prompt}") + +# Execute prompt with completion +response = portkey.prompts.completions( + prompt_id="template_123", + variables={"name": "Alice", "topic": "AI"}, + model="gpt-4", + temperature=0.7, + max_tokens=150 +) + +print(f"Response: {response.choices[0].message.content}") + +# Work with prompt versions +versions = portkey.prompts.versions.list("template_123") +print(f"Available versions: {[v.id for v in versions.data]}") + +# Get specific version +version = portkey.prompts.versions.retrieve("template_123", "v2") +print(f"Version details: {version.prompt}") + +# Stream completion responses +for chunk in portkey.prompts.completions( + prompt_id="template_123", + variables={"name": "Alice", "topic": "AI"}, + model="gpt-4", + stream=True +): + if chunk.choices[0].delta.content: + print(chunk.choices[0].delta.content, end="") +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def manage_prompts(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + # Render prompt asynchronously + rendered = await portkey.prompts.render( + prompt_id="template_123", + variables={"name": "Bob", "topic": "Machine Learning"} + ) + + # Execute prompt completion + response = await portkey.prompts.completions( + prompt_id="template_123", + variables={"name": "Bob", "topic": "Machine Learning"}, + model="gpt-4" + ) + + return rendered, response + +rendered, response = asyncio.run(manage_prompts()) +``` + +## Types + +```python { .api } +class PromptRender: + """Rendered prompt content""" + prompt: str # Rendered prompt text + variables: dict # Variables used in rendering + prompt_id: str # Source prompt template ID + +class PromptCompletion: + """Prompt completion response""" + id: str # Completion ID + object: str # "prompt.completion" + created: int # Unix timestamp + model: str # Model used + choices: List[PromptChoice] # Response choices + usage: dict # Token usage information + prompt_id: str # Source prompt ID + +class PromptCompletionChunk: + """Streaming prompt completion chunk""" + id: str # Completion ID + object: str # "prompt.completion.chunk" + created: int # Unix timestamp + model: str # Model used + choices: List[PromptChoiceDelta] # Response deltas + prompt_id: str # Source prompt ID + +class PromptChoice: + """Prompt completion choice""" + index: int # Choice index + message: dict # Response message + finish_reason: Optional[str] # Completion reason + +class PromptChoiceDelta: + """Streaming choice delta""" + index: int # Choice index + delta: dict # Content delta + finish_reason: Optional[str] # Completion reason +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md new file mode 100644 index 000000000..646debb26 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md @@ -0,0 +1,24 @@ +# Provider & Integration Management + +Management of AI providers and third-party integrations with workspace-level and model-level configuration. + +## Capabilities + +```python { .api } +class Providers: + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class Integrations: + workspaces: IntegrationsWorkspaces + models: IntegrationsModels + +class IntegrationsWorkspaces: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + +class IntegrationsModels: + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md new file mode 100644 index 000000000..5e7b2c16c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md @@ -0,0 +1,354 @@ +# Text Completions + +Legacy text completion API for non-chat models, supporting streaming and all OpenAI-compatible parameters with Portkey enhancements. + +## Capabilities + +### Text Completion Creation + +Generate text completions using prompt-based models with support for streaming, multiple completions, and advanced parameters. + +```python { .api } +class Completion: + """Synchronous text completion API""" + + def create( + self, + *, + prompt: Union[str, List[str]], + model: str, + best_of: Optional[int] = None, + echo: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + logit_bias: Optional[dict] = None, + logprobs: Optional[int] = None, + max_tokens: Optional[int] = None, + n: Optional[int] = None, + presence_penalty: Optional[float] = None, + seed: Optional[int] = None, + stop: Union[Optional[str], List[str]] = None, + stream: Optional[bool] = None, + suffix: Optional[str] = None, + temperature: Optional[float] = None, + top_p: Optional[float] = None, + user: Optional[str] = None, + **kwargs + ) -> Union[TextCompletion, Iterator[TextCompletionChunk]]: + """ + Create a text completion. + + Parameters: + - prompt: Text prompt or list of prompts + - model: Model identifier (e.g., 'text-davinci-003', 'gpt-3.5-turbo-instruct') + - max_tokens: Maximum tokens to generate + - temperature: Sampling temperature (0-2) + - top_p: Nucleus sampling parameter (0-1) + - n: Number of completions to generate + - stream: Enable streaming responses + - stop: Stop sequences + - presence_penalty: Presence penalty (-2 to 2) + - frequency_penalty: Frequency penalty (-2 to 2) + - best_of: Generate best_of completions server-side and return the best + - echo: Echo back the prompt in addition to the completion + - logprobs: Include log probabilities on the logprobs most likely tokens + - logit_bias: Modify likelihood of specified tokens + - suffix: Suffix that comes after completion of inserted text + - seed: Seed for deterministic generation + - user: User identifier for tracking + + Returns: + TextCompletion object or Iterator of TextCompletionChunk for streaming + """ + +class AsyncCompletion: + """Asynchronous text completion API""" + + async def create( + self, + *, + prompt: Union[str, List[str]], + model: str, + **kwargs + ) -> Union[TextCompletion, AsyncIterator[TextCompletionChunk]]: + """Async version of text completion creation""" +``` + +## Usage Examples + +### Basic Text Completion + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Simple text completion +response = portkey.completions.create( + prompt="The future of artificial intelligence is", + model="gpt-3.5-turbo-instruct", + max_tokens=100, + temperature=0.7 +) + +print(response.choices[0].text) +``` + +### Multiple Prompts + +```python +# Multiple prompts in one request +prompts = [ + "The capital of France is", + "The largest planet in our solar system is", + "Python is a programming language that" +] + +response = portkey.completions.create( + prompt=prompts, + model="gpt-3.5-turbo-instruct", + max_tokens=50, + temperature=0.5 +) + +for i, choice in enumerate(response.choices): + print(f"Prompt {i+1}: {choice.text.strip()}") +``` + +### Streaming Completion + +```python +# Streaming text completion +stream = portkey.completions.create( + prompt="Write a short story about a robot discovering emotions:", + model="gpt-3.5-turbo-instruct", + max_tokens=200, + stream=True +) + +for chunk in stream: + if chunk.choices[0].text: + print(chunk.choices[0].text, end="") +``` + +### Code Completion with Suffix + +```python +# Code completion with suffix (fill-in-the-middle) +response = portkey.completions.create( + prompt="def calculate_fibonacci(n):\n if n <= 1:\n return n\n else:\n ", + suffix="\n return result", + model="gpt-3.5-turbo-instruct", + max_tokens=100, + temperature=0.2 +) + +print(response.choices[0].text) +``` + +### Advanced Parameters + +```python +# Using advanced parameters +response = portkey.completions.create( + prompt="Explain quantum computing in simple terms:", + model="gpt-3.5-turbo-instruct", + max_tokens=150, + temperature=0.8, + top_p=0.9, + frequency_penalty=0.1, + presence_penalty=0.1, + best_of=3, # Generate 3 completions, return the best + n=1, # Return 1 completion + echo=True, # Include the prompt in the response + logprobs=3, # Include log probabilities for top 3 tokens + stop=["\n\n", "###"], # Stop at double newline or ### + user="user123" +) + +print("Full response with prompt:", response.choices[0].text) +if response.choices[0].logprobs: + print("Token log probabilities:", response.choices[0].logprobs) +``` + +### Async Usage + +```python +import asyncio +from portkey_ai import AsyncPortkey + +async def completion_example(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + # Async completion + response = await portkey.completions.create( + prompt="The benefits of renewable energy include:", + model="gpt-3.5-turbo-instruct", + max_tokens=100 + ) + + print(response.choices[0].text) + + # Async streaming + async for chunk in await portkey.completions.create( + prompt="List the planets in our solar system:", + model="gpt-3.5-turbo-instruct", + max_tokens=100, + stream=True + ): + if chunk.choices[0].text: + print(chunk.choices[0].text, end="") + +asyncio.run(completion_example()) +``` + +### Deterministic Generation + +```python +# Reproducible completions with seed +response1 = portkey.completions.create( + prompt="Generate a random story idea:", + model="gpt-3.5-turbo-instruct", + max_tokens=50, + temperature=0.9, + seed=42 +) + +response2 = portkey.completions.create( + prompt="Generate a random story idea:", + model="gpt-3.5-turbo-instruct", + max_tokens=50, + temperature=0.9, + seed=42 +) + +# These should be identical +print("Response 1:", response1.choices[0].text) +print("Response 2:", response2.choices[0].text) +``` + +### Logit Bias Control + +```python +# Modify token probabilities with logit bias +response = portkey.completions.create( + prompt="The weather today is", + model="gpt-3.5-turbo-instruct", + max_tokens=20, + logit_bias={ + # Increase likelihood of positive weather words + 1180: 2, # "sunny" + 4771: 2, # "beautiful" + # Decrease likelihood of negative weather words + 21281: -2, # "rainy" + 4172: -2 # "cloudy" + } +) + +print(response.choices[0].text) +``` + +## Model Compatibility + +### Supported Models + +Text completions work with various model types: + +```python +# GPT-3.5 Instruct models +model = "gpt-3.5-turbo-instruct" + +# Legacy GPT-3 models (deprecated) +model = "text-davinci-003" +model = "text-curie-001" +model = "text-babbage-001" +model = "text-ada-001" + +# Code-specific models +model = "code-davinci-002" +model = "code-cushman-001" + +# Provider-specific models through Portkey +model = "anthropic/claude-instant-1" +model = "cohere/command" +model = "huggingface/gpt2" +``` + +### Migration from Legacy Models + +```python +# Old GPT-3 usage +response = portkey.completions.create( + prompt="Translate to French: Hello, how are you?", + model="text-davinci-003", + max_tokens=60 +) + +# Recommended migration to GPT-3.5 Instruct +response = portkey.completions.create( + prompt="Translate to French: Hello, how are you?", + model="gpt-3.5-turbo-instruct", + max_tokens=60 +) + +# Or better: migrate to chat completions +response = portkey.chat.completions.create( + messages=[ + {"role": "user", "content": "Translate to French: Hello, how are you?"} + ], + model="gpt-3.5-turbo", + max_tokens=60 +) +``` + +## Common Use Cases + +### Text Generation + +```python +# Creative writing +response = portkey.completions.create( + prompt="Write a haiku about programming:", + model="gpt-3.5-turbo-instruct", + max_tokens=30, + temperature=0.8 +) + +# Technical documentation +response = portkey.completions.create( + prompt="Function documentation for bubble sort algorithm:", + model="gpt-3.5-turbo-instruct", + max_tokens=100, + temperature=0.3 +) +``` + +### Code Completion + +```python +# Python function completion +response = portkey.completions.create( + prompt="def merge_sort(arr):\n if len(arr) <= 1:\n return arr\n \n mid = len(arr) // 2\n left = arr[:mid]\n right = arr[mid:]\n \n ", + model="gpt-3.5-turbo-instruct", + max_tokens=200, + temperature=0.2, + stop=["\n\n"] +) +``` + +### Data Processing + +```python +# Text classification +response = portkey.completions.create( + prompt="Classify the sentiment of this text as positive, negative, or neutral:\n\n\"I absolutely love this new restaurant! The food was amazing.\"\n\nSentiment:", + model="gpt-3.5-turbo-instruct", + max_tokens=10, + temperature=0 +) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md new file mode 100644 index 000000000..c868e3ee2 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md @@ -0,0 +1,376 @@ +# Uploads + +Large file upload system with support for chunked uploads and multi-part file handling. Enables efficient upload of large files through resumable, chunked upload process with integrity verification. + +## Capabilities + +### Upload Management + +Create, manage, and complete large file uploads using multi-part upload process for improved reliability and performance. + +```python { .api } +class Uploads: + def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: Any, + **kwargs + ) -> Upload: + """ + Create a new upload session for large files. + + Args: + bytes: Total size of the file in bytes + filename: Name of the file being uploaded + mime_type: MIME type of the file (e.g., "image/jpeg", "application/pdf") + purpose: Purpose of the upload (e.g., "assistants", "fine-tune") + **kwargs: Additional upload parameters + + Returns: + Upload: Upload session object with upload ID and status + """ + + def upload_file_chunked( + self, + *, + file: Union[os.PathLike[str], bytes], + mime_type: str, + purpose: Any, + filename: Union[str, None] = None, + bytes: Union[int, None] = None, + part_size: Union[int, None] = None, + md5: Union[str, NotGiven] = NOT_GIVEN + ) -> Any: + """ + Upload a file using automatic chunking for large files. + + Args: + file: File path or file content bytes + mime_type: MIME type of the file + purpose: Purpose of the upload + filename: Optional filename override + bytes: Optional file size override + part_size: Size of each chunk (default: 8MB) + md5: MD5 hash for integrity verification + + Returns: + File object after successful upload + """ + + def complete( + self, + upload_id: str, + *, + part_ids: List[str], + md5: Union[str, NotGiven] = NOT_GIVEN, + **kwargs + ) -> Upload: + """ + Complete a multi-part upload by assembling all parts. + + Args: + upload_id: Upload session identifier + part_ids: List of part IDs in order + md5: MD5 hash of complete file for verification + **kwargs: Additional completion parameters + + Returns: + Upload: Completed upload object + """ + + def cancel(self, upload_id: str, **kwargs) -> Upload: + """ + Cancel an in-progress upload session. + + Args: + upload_id: Upload session identifier + **kwargs: Additional cancellation parameters + + Returns: + Upload: Cancelled upload object + """ + + parts: Parts + +class AsyncUploads: + async def create( + self, + *, + bytes: int, + filename: str, + mime_type: str, + purpose: Any, + **kwargs + ) -> Upload: + """Async version of create method.""" + + async def upload_file_chunked( + self, + *, + file: Union[os.PathLike[str], bytes], + mime_type: str, + purpose: Any, + filename: Union[str, None] = None, + bytes: Union[int, None] = None, + part_size: Union[int, None] = None, + md5: Union[str, NotGiven] = NOT_GIVEN + ) -> Any: + """Async version of chunked upload method.""" + + async def complete( + self, + upload_id: str, + *, + part_ids: List[str], + md5: Union[str, NotGiven] = NOT_GIVEN, + **kwargs + ) -> Upload: + """Async version of complete method.""" + + async def cancel(self, upload_id: str, **kwargs) -> Upload: + """Async version of cancel method.""" + + parts: AsyncParts +``` + +### Part Management + +Handle individual parts of multi-part uploads for fine-grained control over the upload process. + +```python { .api } +class Parts: + def create( + self, + upload_id: str, + *, + data: FileTypes, + **kwargs + ) -> UploadPart: + """ + Upload a single part of a multi-part upload. + + Args: + upload_id: Upload session identifier + data: Part data as file-like object or bytes + **kwargs: Additional part parameters + + Returns: + UploadPart: Part object with part ID and status + """ + +class AsyncParts: + async def create( + self, + upload_id: str, + *, + data: FileTypes, + **kwargs + ) -> UploadPart: + """Async version of part creation.""" +``` + +### Usage Examples + +```python +import os +from portkey_ai import Portkey + +# Initialize client +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Simple chunked upload (recommended for most use cases) +with open("large_document.pdf", "rb") as f: + uploaded_file = portkey.uploads.upload_file_chunked( + file=f, + mime_type="application/pdf", + purpose="assistants", + filename="large_document.pdf" + ) + +print(f"File uploaded successfully: {uploaded_file.id}") + +# Manual multi-part upload for advanced use cases +file_path = "very_large_file.zip" +file_size = os.path.getsize(file_path) + +# Create upload session +upload = portkey.uploads.create( + bytes=file_size, + filename="very_large_file.zip", + mime_type="application/zip", + purpose="assistants" +) + +print(f"Upload session created: {upload.id}") + +# Upload parts manually (8MB chunks) +part_size = 8 * 1024 * 1024 # 8MB +part_ids = [] + +with open(file_path, "rb") as f: + part_number = 1 + while True: + chunk = f.read(part_size) + if not chunk: + break + + part = portkey.uploads.parts.create( + upload_id=upload.id, + data=chunk + ) + part_ids.append(part.id) + print(f"Uploaded part {part_number}: {part.id}") + part_number += 1 + +# Complete the upload +completed_upload = portkey.uploads.complete( + upload_id=upload.id, + part_ids=part_ids +) + +print(f"Upload completed: {completed_upload.status}") +print(f"File ID: {completed_upload.file.id}") +``` + +### Async Usage + +```python +import asyncio +import os +from portkey_ai import AsyncPortkey + +async def upload_large_file(): + portkey = AsyncPortkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" + ) + + # Async chunked upload + with open("large_dataset.csv", "rb") as f: + uploaded_file = await portkey.uploads.upload_file_chunked( + file=f, + mime_type="text/csv", + purpose="fine-tune", + part_size=16 * 1024 * 1024 # 16MB chunks + ) + + return uploaded_file + +# Run async upload +uploaded_file = asyncio.run(upload_large_file()) +print(f"Async upload completed: {uploaded_file.id}") +``` + +### Error Handling and Resumption + +```python +import hashlib +import os + +def upload_with_verification(): + file_path = "important_document.pdf" + + # Calculate MD5 for integrity verification + with open(file_path, "rb") as f: + file_hash = hashlib.md5(f.read()).hexdigest() + + file_size = os.path.getsize(file_path) + + try: + # Create upload with MD5 + upload = portkey.uploads.create( + bytes=file_size, + filename=os.path.basename(file_path), + mime_type="application/pdf", + purpose="assistants" + ) + + # Upload with chunked method including hash + with open(file_path, "rb") as f: + uploaded_file = portkey.uploads.upload_file_chunked( + file=f, + mime_type="application/pdf", + purpose="assistants", + md5=file_hash + ) + + print(f"Upload successful with verification: {uploaded_file.id}") + return uploaded_file + + except Exception as e: + print(f"Upload failed: {e}") + # Cancel the upload session if it was created + if 'upload' in locals(): + try: + cancelled = portkey.uploads.cancel(upload.id) + print(f"Upload cancelled: {cancelled.status}") + except Exception as cancel_error: + print(f"Failed to cancel upload: {cancel_error}") + + raise + +# Example with retry logic +def upload_with_retry(file_path, max_retries=3): + for attempt in range(max_retries): + try: + return upload_with_verification() + except Exception as e: + print(f"Attempt {attempt + 1} failed: {e}") + if attempt == max_retries - 1: + raise + # Wait before retry + import time + time.sleep(2 ** attempt) +``` + +## Types + +```python { .api } +class Upload: + """Upload session object""" + id: str # Upload identifier + object: str # "upload" + bytes: int # Total file size + created_at: int # Unix timestamp + filename: str # Original filename + purpose: str # Upload purpose + status: str # Upload status ("pending", "completed", "cancelled", "failed") + expires_at: int # Session expiration timestamp + file: Optional[FileObject] # File object after completion + _headers: Optional[dict] # Response headers + +class UploadPart: + """Individual upload part""" + id: str # Part identifier + object: str # "upload.part" + created_at: int # Unix timestamp + upload_id: str # Parent upload identifier + etag: str # Part ETag for integrity + _headers: Optional[dict] # Response headers + +class FileObject: + """Completed file object""" + id: str # File identifier + object: str # "file" + bytes: int # File size + created_at: int # Unix timestamp + filename: str # File name + purpose: str # File purpose + status: str # Processing status + status_details: Optional[str] # Status details if applicable + +FileTypes = Union[ + bytes, + str, + os.PathLike[str], + typing.IO[bytes], + typing.IO[str] +] +"""Supported file input types for uploads""" +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md new file mode 100644 index 000000000..43b6a2e0a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md @@ -0,0 +1,58 @@ +# Vector Stores + +Vector database operations for retrieval-augmented generation (RAG) applications with file management and batch processing. + +## Capabilities + +### Vector Store Management + +```python { .api } +class VectorStores: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def update(self, **kwargs): ... + def delete(self, **kwargs): ... + files: VectorFiles + file_batches: VectorFileBatches + +class VectorFiles: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def delete(self, **kwargs): ... + +class VectorFileBatches: + def create(self, **kwargs): ... + def list(self, **kwargs): ... + def retrieve(self, **kwargs): ... + def cancel(self, **kwargs): ... +``` + +## Usage Examples + +```python +from portkey_ai import Portkey + +portkey = Portkey( + api_key="PORTKEY_API_KEY", + virtual_key="VIRTUAL_KEY" +) + +# Create vector store +vector_store = portkey.beta.vector_stores.create( + name="Knowledge Base" +) + +# Add files to vector store +portkey.beta.vector_stores.files.create( + vector_store_id=vector_store.id, + file_id="file-123" +) + +# Create file batch +batch = portkey.beta.vector_stores.file_batches.create( + vector_store_id=vector_store.id, + file_ids=["file-123", "file-456"] +) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/tile.json b/.tessl/tiles/tessl/pypi-portkey-ai/tile.json new file mode 100644 index 000000000..636ab7d07 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-portkey-ai/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-portkey-ai", + "version": "1.14.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/portkey-ai@1.14.4", + "summary": "Python client library for the Portkey API - Control Panel for AI Apps with unified API signature, automated fallbacks, retries, load balancing, semantic caching, virtual keys, and comprehensive observability features." +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md new file mode 100644 index 000000000..1cb715f5c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md @@ -0,0 +1,311 @@ +# CLI Commands + +Complete command-line interface for pre-commit hook management. All commands are executed through the `pre-commit` console script entry point, which routes to the appropriate command implementations. + +## Capabilities + +### Main Entry Point + +Primary entry point for all CLI commands that parses arguments and routes to appropriate command handlers. + +```python { .api } +def main(argv: Sequence[str] | None = None) -> int: + """ + Main CLI entry point for pre-commit commands. + + Parameters: + - argv: Optional command-line arguments array + + Returns: + - int: Exit code (0 for success, non-zero for failure) + """ +``` + +### Installation Commands + +Commands for installing and managing pre-commit hook scripts in Git repositories. + +```python { .api } +def install( + config_file: str, + store: Store, + hook_types: list[str] | None, + overwrite: bool = False, + hooks: bool = False, + skip_on_missing_config: bool = False, + git_dir: str | None = None, +) -> int: + """ + Install git hook scripts. + + Parameters: + - config_file: Path to configuration file + - store: Store instance for hook management + - hook_types: List of git hook types to install + - overwrite: Whether to overwrite existing hooks + - hooks: Whether to install hook environments too + - skip_on_missing_config: Skip installation if config missing + - git_dir: Git directory path (optional) + + Returns: + - int: Exit code + """ + +def uninstall(config_file: str, hook_types: list[str] | None) -> int: + """ + Remove git hook scripts. + + Parameters: + - config_file: Path to configuration file + - hook_types: List of git hook types to uninstall + + Returns: + - int: Exit code + """ + +def install_hooks(config_file: str, store: Store) -> int: + """ + Install hook environments for all configured hooks. + + Parameters: + - config_file: Path to configuration file + - store: Store instance for hook management + + Returns: + - int: Exit code + """ + +def init_templatedir( + config_file: str, + store: Store, + directory: str, + hook_types: list[str] | None, + skip_on_missing_config: bool = True, +) -> int: + """ + Setup hooks in git template directory. + + Parameters: + - config_file: Path to configuration file + - store: Store instance for hook management + - directory: Template directory path + - hook_types: List of git hook types to install + - skip_on_missing_config: Skip installation if config missing + + Returns: + - int: Exit code + """ +``` + +### Hook Execution Commands + +Commands for running pre-commit hooks on files with various filtering and execution options. + +```python { .api } +def run( + config_file: str, + store: Store, + args: argparse.Namespace, + environ: MutableMapping[str, str] = os.environ, +) -> int: + """ + Execute hooks with file filtering and staging options. + + Parameters: + - config_file: Path to configuration file + - store: Store instance for hook management + - all_files: Run on all files instead of just staged + - files: Specific files to run on + - hook_stage: Git hook stage to run + - hook: Run only specific hook by id + - from_ref: Git ref to compare from + - to_ref: Git ref to compare to + - show_diff_on_failure: Show diff when hooks fail + - color: Color output mode ('auto', 'always', 'never') + - verbose: Verbose output + + Returns: + - int: Exit code (0 if all hooks pass) + """ + +def hook_impl(config_file: str, hook_type: str, hook_dir: str | None = None, + skip_on_missing_config: bool = False, **kwargs) -> int: + """ + Internal hook implementation (not for direct user use). + + Parameters: + - config_file: Path to configuration file + - hook_type: Type of git hook being executed + - hook_dir: Hook directory path + - skip_on_missing_config: Skip if config missing + + Returns: + - int: Exit code + """ +``` + +### Configuration Management Commands + +Commands for managing, validating, and updating pre-commit configuration files. + +```python { .api } +def autoupdate(config_file: str = '.pre-commit-config.yaml', store: Store | None = None, + tags_only: bool = False, bleeding_edge: bool = False, + freeze: bool = False, repo: Sequence[str] = (), + jobs: int = 1) -> int: + """ + Auto-update repository versions in configuration. + + Parameters: + - config_file: Path to configuration file + - store: Store instance for repository management + - tags_only: Only use tagged versions + - bleeding_edge: Use latest commit instead of latest tag + - freeze: Store exact commit hash instead of tag + - repo: Update only specific repositories + - jobs: Number of concurrent jobs + + Returns: + - int: Exit code + """ + +def migrate_config(config_file: str = '.pre-commit-config.yaml') -> int: + """ + Migrate configuration file to latest format. + + Parameters: + - config_file: Path to configuration file + + Returns: + - int: Exit code + """ + +def validate_config(filenames: Sequence[str]) -> int: + """ + Validate .pre-commit-config.yaml files. + + Parameters: + - filenames: List of config files to validate + + Returns: + - int: Exit code (0 if all valid) + """ + +def validate_manifest(filenames: Sequence[str]) -> int: + """ + Validate .pre-commit-hooks.yaml manifest files. + + Parameters: + - filenames: List of manifest files to validate + + Returns: + - int: Exit code (0 if all valid) + """ +``` + +### Utility Commands + +Utility commands for testing, configuration generation, and maintenance operations. + +```python { .api } +def sample_config() -> int: + """ + Generate sample .pre-commit-config.yaml configuration. + + Returns: + - int: Exit code + """ + +def try_repo(repo: str, ref: str = 'HEAD', **kwargs) -> int: + """ + Test hooks from a repository without modifying configuration. + + Parameters: + - repo: Repository URL or path + - ref: Git reference to use + + Returns: + - int: Exit code + """ + +def clean(store: Store | None = None) -> int: + """ + Clean pre-commit cache and temporary files. + + Parameters: + - store: Store instance for cleanup operations + + Returns: + - int: Exit code + """ + +def gc(store: Store | None = None) -> int: + """ + Garbage collect unused repositories and environments. + + Parameters: + - store: Store instance for garbage collection + + Returns: + - int: Exit code + """ +``` + +## Hook Types and Stages + +### Supported Git Hook Types + +```python { .api } +HOOK_TYPES = ( + 'commit-msg', 'post-checkout', 'post-commit', 'post-merge', + 'post-rewrite', 'pre-commit', 'pre-merge-commit', 'pre-push', + 'pre-rebase', 'prepare-commit-msg' +) + +STAGES = (*HOOK_TYPES, 'manual') +``` + +### Commands Without Git Repository Requirement + +```python { .api } +COMMANDS_NO_GIT = { + 'clean', 'gc', 'init-templatedir', 'sample-config', + 'validate-config', 'validate-manifest', +} +``` + +## Usage Examples + +### Basic Command Usage + +```python +from pre_commit.main import main + +# Install hooks +exit_code = main(['install']) + +# Run all hooks +exit_code = main(['run', '--all-files']) + +# Auto-update configuration +exit_code = main(['autoupdate']) + +# Validate configuration +exit_code = main(['validate-config', '.pre-commit-config.yaml']) +``` + +### Advanced Command Usage + +```python +# Run specific hook with verbose output +exit_code = main(['run', '--hook', 'flake8', '--verbose']) + +# Run on specific files +exit_code = main(['run', '--files', 'file1.py', 'file2.py']) + +# Show diff on failure +exit_code = main(['run', '--show-diff-on-failure']) + +# Try hooks from external repository +exit_code = main(['try-repo', 'https://github.com/psf/black']) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md new file mode 100644 index 000000000..3dcbbdc5f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md @@ -0,0 +1,241 @@ +# Configuration Management + +Configuration loading, validation, and schema definitions for pre-commit configuration files. Pre-commit uses YAML-based configuration files to define hooks and their settings. + +## Capabilities + +### Configuration Loading + +Functions for loading and parsing pre-commit configuration files with automatic validation. + +```python { .api } +load_config: functools.partial[dict[str, Any]] +# Partial function for loading .pre-commit-config.yaml files +# Usage: config = load_config(filename) +# Raises InvalidConfigError if configuration is invalid + +load_manifest: functools.partial[list[dict[str, Any]]] +# Partial function for loading .pre-commit-hooks.yaml files +# Usage: hooks = load_manifest(filename) +# Raises InvalidManifestError if manifest is invalid +``` + +### Configuration Validation Functions + +Utility functions for validating configuration elements. + +```python { .api } +def check_type_tag(tag: str) -> None: + """Validate file type tag is recognized""" + +def parse_version(s: str) -> tuple[int, ...]: + """Parse version string for comparison""" + +def check_min_version(version: str) -> None: + """Check minimum pre-commit version requirement""" + +def transform_stage(stage: str) -> str: + """Transform legacy stage names to current format""" +``` + +### Schema Constants + +JSON schemas used for validating configuration and manifest files. + +```python { .api } +CONFIG_SCHEMA: cfgv.Map +"""Schema for .pre-commit-config.yaml validation""" + +MANIFEST_SCHEMA: cfgv.Array +"""Schema for .pre-commit-hooks.yaml validation""" + +MINIMAL_MANIFEST_SCHEMA: cfgv.Array +"""Minimal schema for manifest validation""" +``` + +## Configuration File Formats + +### Pre-commit Configuration (.pre-commit-config.yaml) + +Main configuration file format for defining repositories and hooks: + +```yaml +# .pre-commit-config.yaml example +repos: + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black + args: [--line-length=88] + files: \.py$ + - repo: https://github.com/pycqa/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + additional_dependencies: [flake8-docstrings] +``` + +#### Configuration Schema Properties + +```python { .api } +class ConfigRepo: + """Configuration repository definition""" + repo: str # Repository URL or 'local' + rev: str # Git revision (tag, commit, branch) + hooks: list[ConfigHook] # List of hooks from this repo + +class ConfigHook: + """Hook configuration in .pre-commit-config.yaml""" + id: str # Hook identifier + alias: str | None = None # Alternative name + name: str | None = None # Display name override + language_version: str | None = None # Language version + files: str | None = None # File pattern regex + exclude: str | None = None # Exclusion pattern + types: list[str] | None = None # File types to include + types_or: list[str] | None = None # Alternative file types + exclude_types: list[str] | None = None # File types to exclude + args: list[str] | None = None # Additional arguments + stages: list[str] | None = None # Git hook stages + additional_dependencies: list[str] | None = None # Extra deps + always_run: bool | None = None # Run even without files + verbose: bool | None = None # Verbose output + log_file: str | None = None # Log file path +``` + +### Hook Manifest (.pre-commit-hooks.yaml) + +Manifest file format for defining available hooks in a repository: + +```yaml +# .pre-commit-hooks.yaml example +- id: black + name: black + description: "The uncompromising Python code formatter" + entry: black + language: python + require_serial: false + types_or: [python, pyi] + minimum_pre_commit_version: 2.9.2 +``` + +#### Manifest Schema Properties + +```python { .api } +class ManifestHook: + """Hook definition in .pre-commit-hooks.yaml""" + id: str # Unique identifier + name: str # Display name + entry: str # Command to execute + language: str # Programming language + description: str | None = None # Hook description + alias: str | None = None # Alternative name + files: str | None = None # File pattern regex + exclude: str | None = None # Exclusion pattern + types: list[str] | None = None # File types + types_or: list[str] | None = None # Alternative file types + exclude_types: list[str] | None = None # Excluded file types + always_run: bool = False # Always execute + fail_fast: bool = False # Stop on first failure + pass_filenames: bool = True # Pass filenames to hook + require_serial: bool = False # Serial execution required + stages: list[str] | None = None # Applicable stages + args: list[str] | None = None # Default arguments + verbose: bool = False # Verbose output + language_version: str = 'default' # Language version + log_file: str | None = None # Log file path + minimum_pre_commit_version: str = '0.15.0' # Version requirement + additional_dependencies: list[str] = [] # Extra dependencies +``` + +## File Type Detection + +### Supported File Types + +Pre-commit includes built-in file type detection for various programming languages and file formats: + +```python { .api } +FILE_TYPES = [ + 'bash', 'batch', 'c', 'c++', 'csharp', 'css', 'dart', 'dockerfile', + 'elixir', 'go', 'haskell', 'html', 'java', 'javascript', 'json', + 'jsx', 'kotlin', 'lua', 'markdown', 'perl', 'php', 'python', 'r', + 'ruby', 'rust', 'scala', 'shell', 'sql', 'swift', 'text', 'toml', + 'tsx', 'typescript', 'xml', 'yaml' +] +``` + +## Configuration Examples + +### Loading Configuration + +```python +from pre_commit.clientlib import load_config, InvalidConfigError + +try: + config = load_config('.pre-commit-config.yaml') + print(f"Found {len(config['repos'])} repositories") + + for repo in config['repos']: + print(f"Repository: {repo['repo']}") + print(f"Hooks: {[hook['id'] for hook in repo['hooks']]}") + +except InvalidConfigError as e: + print(f"Configuration error: {e}") +``` + +### Loading Manifest + +```python +from pre_commit.clientlib import load_manifest, InvalidManifestError + +try: + manifest = load_manifest('.pre-commit-hooks.yaml') + print(f"Found {len(manifest)} hook definitions") + + for hook in manifest: + print(f"Hook: {hook['id']} ({hook['language']})") + +except InvalidManifestError as e: + print(f"Manifest error: {e}") +``` + +### Configuration Validation Functions + +```python +from pre_commit.clientlib import check_type_tag, parse_version, check_min_version + +# Validate file type tags +try: + check_type_tag('python') # Valid tag + check_type_tag('invalid') # Would raise ValidationError +except cfgv.ValidationError as e: + print(f"Invalid type tag: {e}") + +# Parse and compare versions +version_tuple = parse_version('1.2.3') # Returns (1, 2, 3) + +# Check minimum version requirement +try: + check_min_version('2.0.0') +except cfgv.ValidationError as e: + print(f"Version too old: {e}") +``` + +## Constants + +### Configuration File Names + +```python { .api } +CONFIG_FILE: str = '.pre-commit-config.yaml' +"""Default configuration file name""" + +MANIFEST_FILE: str = '.pre-commit-hooks.yaml' +"""Default manifest file name""" +``` + +### Validation Settings + +```python { .api } +DEFAULT_LANGUAGE_VERSION: cfgv.Map +"""Schema for default language version configuration""" +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md new file mode 100644 index 000000000..0d763ecdc --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md @@ -0,0 +1,393 @@ +# Git Integration + +Git repository utilities and integration functions for working with staged files, repository state, and Git workflow integration. Pre-commit provides deep integration with Git operations to enable seamless hook execution within development workflows. + +## Capabilities + +### Repository Information + +Functions for retrieving basic repository information and paths. + +```python { .api } +def get_root() -> str: + """ + Get the root directory of the current Git repository. + + Returns: + - str: Absolute path to repository root + + Raises: + - CalledProcessError: If not in a Git repository + """ + +def get_git_dir() -> str: + """ + Get the .git directory path for the current repository. + + Returns: + - str: Path to .git directory + """ + +def get_git_common_dir() -> str: + """ + Get the common Git directory (for worktrees). + + Returns: + - str: Path to common Git directory + """ +``` + +### File Status and Listing + +Functions for retrieving file lists based on Git status and staging state. + +```python { .api } +def get_staged_files(cwd: str | None = None) -> list[str]: + """ + Get list of files currently staged for commit. + + Parameters: + - cwd: Working directory (defaults to repository root) + + Returns: + - list: Staged file paths relative to repository root + """ + +def get_all_files() -> list[str]: + """ + Get all tracked files in the repository. + + Returns: + - list: All tracked file paths relative to repository root + """ + +def get_changed_files(old: str, new: str) -> list[str]: + """ + Get files changed between two Git references. + + Parameters: + - old: Old Git reference (commit, branch, tag) + - new: New Git reference (commit, branch, tag) + + Returns: + - list: Changed file paths between references + """ + +def get_conflicted_files() -> set[str]: + """ + Get files currently in merge conflict state. + + Returns: + - set: File paths with unresolved conflicts + """ +``` + +### Repository State + +Functions for checking repository state and conditions. + +```python { .api } +def is_in_merge_conflict() -> bool: + """ + Check if repository is currently in a merge conflict state. + + Returns: + - bool: True if there are unresolved merge conflicts + """ + +def is_in_rebase() -> bool: + """ + Check if repository is currently in a rebase operation. + + Returns: + - bool: True if rebase is in progress + """ + +def is_in_cherry_pick() -> bool: + """ + Check if repository is currently in a cherry-pick operation. + + Returns: + - bool: True if cherry-pick is in progress + """ + +def has_unmerged_paths() -> bool: + """ + Check if repository has unmerged paths. + + Returns: + - bool: True if there are unmerged paths + """ +``` + +### Git Configuration + +Functions for reading and checking Git configuration settings. + +```python { .api } +def has_core_hookpaths_set() -> bool: + """ + Check if Git core.hooksPath configuration is set. + + Pre-commit needs to know if custom hook paths are configured + to handle installation correctly. + + Returns: + - bool: True if core.hooksPath is configured + """ + +def get_hook_paths() -> list[str]: + """ + Get configured Git hook paths. + + Returns: + - list: Hook directory paths + """ +``` + +### Commit and Reference Operations + +Functions for working with commits, branches, and references. + +```python { .api } +def get_commit_msg(commit_msg_filename: str) -> str: + """ + Read commit message from file. + + Parameters: + - commit_msg_filename: Path to commit message file + + Returns: + - str: Commit message content + """ + +def set_commit_msg(commit_msg_filename: str, new_msg: str) -> None: + """ + Write commit message to file. + + Parameters: + - commit_msg_filename: Path to commit message file + - new_msg: New commit message content + """ + +def get_staged_files_intent_to_add() -> list[str]: + """ + Get files staged with intent-to-add flag. + + Returns: + - list: File paths staged with git add --intent-to-add + """ +``` + +### Git Command Utilities + +Low-level utilities for executing Git commands and processing output. + +```python { .api } +def zsplit(s: str) -> list[str]: + """ + Split null-separated string into list. + + Used for processing Git output with null separators. + + Parameters: + - s: Null-separated string + + Returns: + - list: Split string components + """ + +def no_git_env(**kwargs) -> dict[str, str]: + """ + Create environment dictionary with Git variables removed. + + Useful for running commands without Git environment influence. + + Parameters: + - kwargs: Additional environment variables + + Returns: + - dict: Environment without Git variables + """ +``` + +### File Modification Detection + +Functions for detecting file changes and modifications. + +```python { .api } +def get_diff_files(ref1: str, ref2: str) -> list[str]: + """ + Get files different between two references. + + Parameters: + - ref1: First Git reference + - ref2: Second Git reference + + Returns: + - list: Files with differences + """ + +def intent_to_add_cleared() -> bool: + """ + Check if intent-to-add files have been cleared from index. + + Returns: + - bool: True if intent-to-add state is clear + """ +``` + +## Git Hook Integration + +### Hook Installation Paths + +Functions for determining where Git hooks should be installed. + +```python { .api } +def get_hook_script_path(hook_type: str) -> str: + """ + Get the file path where a Git hook script should be installed. + + Parameters: + - hook_type: Type of Git hook (pre-commit, pre-push, etc.) + + Returns: + - str: Path to hook script file + """ + +def hook_exists(hook_type: str) -> bool: + """ + Check if a Git hook script already exists. + + Parameters: + - hook_type: Type of Git hook to check + + Returns: + - bool: True if hook script exists + """ +``` + +### Git Object Utilities + +Functions for working with Git objects and SHA values. + +```python { .api } +def get_object_name(ref: str) -> str: + """ + Get the SHA hash for a Git reference. + + Parameters: + - ref: Git reference (branch, tag, commit) + + Returns: + - str: SHA hash of the object + """ + +def is_valid_sha(sha: str) -> bool: + """ + Check if string is a valid Git SHA hash. + + Parameters: + - sha: String to validate + + Returns: + - bool: True if valid SHA format + """ +``` + +## Usage Examples + +### Working with Staged Files + +```python +from pre_commit import git + +# Get all staged files for processing +staged_files = git.get_staged_files() +print(f"Processing {len(staged_files)} staged files:") +for file_path in staged_files: + print(f" {file_path}") + +# Check if we're in a merge conflict +if git.is_in_merge_conflict(): + print("Repository is in merge conflict state") + conflicted = git.get_conflicted_files() + print(f"Conflicted files: {conflicted}") +else: + print("Repository is in clean state") +``` + +### Repository Information + +```python +from pre_commit import git + +# Get repository root +repo_root = git.get_root() +print(f"Repository root: {repo_root}") + +# Check Git configuration +if git.has_core_hookpaths_set(): + print("Custom hook paths are configured") + paths = git.get_hook_paths() + print(f"Hook paths: {paths}") +else: + print("Using default Git hook paths") +``` + +### File Change Detection + +```python +from pre_commit import git + +# Get all tracked files +all_files = git.get_all_files() +print(f"Repository contains {len(all_files)} tracked files") + +# Get changes between commits +changed_files = git.get_changed_files('HEAD~1', 'HEAD') +print(f"Files changed in last commit: {len(changed_files)}") +for file_path in changed_files: + print(f" {file_path}") + +# Get files staged with intent-to-add +intent_files = git.get_staged_files_intent_to_add() +if intent_files: + print(f"Files staged with intent-to-add: {intent_files}") +``` + +### Environment Management + +```python +from pre_commit import git +import subprocess + +# Run command without Git environment variables +env = git.no_git_env(CUSTOM_VAR='value') +result = subprocess.run( + ['some-command'], + env=env, + capture_output=True, + text=True +) +``` + +### Repository State Checking + +```python +from pre_commit import git + +# Check various repository states +states = { + 'merge_conflict': git.is_in_merge_conflict(), + 'rebase': git.is_in_rebase(), + 'cherry_pick': git.is_in_cherry_pick(), + 'unmerged_paths': git.has_unmerged_paths() +} + +active_states = [name for name, active in states.items() if active] +if active_states: + print(f"Repository states: {', '.join(active_states)}") +else: + print("Repository is in normal state") +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md new file mode 100644 index 000000000..8406ac793 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md @@ -0,0 +1,381 @@ +# Hook System + +Core data structures and management functions for pre-commit hooks. The hook system provides the foundation for defining, configuring, and executing individual hooks within the pre-commit framework. + +## Capabilities + +### Hook Data Structure + +Core data structure representing a configured hook with all its properties and execution parameters. + +```python { .api } +class Hook(NamedTuple): + """ + Immutable data structure representing a configured pre-commit hook. + + All hook properties are defined at initialization and cannot be modified + during execution, ensuring consistent behavior across hook runs. + """ + src: str # Repository source URL or 'local' + prefix: Prefix # Installation directory prefix + id: str # Unique hook identifier + name: str # Display name for output + entry: str # Command or script to execute + language: str # Programming language (python, node, etc.) + alias: str # Alternative name for hook + files: str # File pattern regex for inclusion + exclude: str # File pattern regex for exclusion + types: Sequence[str] # File types to include + types_or: Sequence[str] # Alternative file types (OR logic) + exclude_types: Sequence[str] # File types to exclude + additional_dependencies: Sequence[str] # Extra dependencies to install + args: Sequence[str] # Command-line arguments + always_run: bool # Run even if no files match + fail_fast: bool # Stop execution on first failure + pass_filenames: bool # Pass matched filenames to hook + description: str # Human-readable description + language_version: str # Specific language version + log_file: str # Output log file path + minimum_pre_commit_version: str # Required pre-commit version + require_serial: bool # Require serial execution + stages: Sequence[str] # Git hook stages when this runs + verbose: bool # Enable verbose output +``` + +### Prefix System + +Directory management system for hook installations and environments. + +```python { .api } +class Prefix(NamedTuple): + """ + Directory path management for hook installations. + + Provides utilities for constructing paths within hook installation + directories and checking for file existence. + """ + prefix_dir: str # Base installation directory + + def path(self, *parts: str) -> str: + """ + Construct path within prefix directory. + + Parameters: + - parts: Path components to join + + Returns: + - str: Complete path within prefix + """ + + def exists(self, *parts: str) -> bool: + """ + Check if path exists within prefix directory. + + Parameters: + - parts: Path components to check + + Returns: + - bool: True if path exists + """ + + def star(self, end: str) -> tuple[str, ...]: + """ + Glob pattern matching within prefix directory. + + Parameters: + - end: Pattern to match + + Returns: + - tuple: Matching file paths + """ +``` + +### Hook Management Functions + +Functions for processing and managing collections of hooks. + +```python { .api } +def all_hooks(root_config: dict[str, Any], store: Store) -> tuple[Hook, ...]: + """ + Extract all configured hooks from configuration. + + Processes the configuration file and creates Hook instances for all + defined hooks, handling repository cloning and hook resolution. + + Parameters: + - root_config: Loaded configuration dictionary + - store: Store instance for repository management + + Returns: + - tuple: All configured Hook instances + """ + +def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None: + """ + Install environments for all provided hooks. + + Sets up the necessary runtime environments for each hook, + including language-specific dependencies and tools. + + Parameters: + - hooks: Sequence of hooks to install environments for + - store: Store instance for environment management + """ +``` + +### Hook Filtering and Selection + +Functions for filtering hooks based on various criteria. + +```python { .api } +def filter_by_include_exclude( + hook: Hook, + filenames: Sequence[str], + include: str = '', + exclude: str = '' +) -> Sequence[str]: + """ + Filter filenames based on hook's include/exclude patterns. + + Parameters: + - hook: Hook with filtering configuration + - filenames: Files to filter + - include: Additional include pattern + - exclude: Additional exclude pattern + + Returns: + - Sequence: Filtered filenames + """ + +def classify_by_types( + filenames: Sequence[str], + types: Sequence[str], + types_or: Sequence[str] = (), + exclude_types: Sequence[str] = () +) -> Sequence[str]: + """ + Classify and filter files by type. + + Parameters: + - filenames: Files to classify + - types: Required file types (AND logic) + - types_or: Alternative file types (OR logic) + - exclude_types: File types to exclude + + Returns: + - Sequence: Files matching type criteria + """ +``` + +## Hook Execution + +### Hook Runner + +Core hook execution functionality with environment management and output handling. + +```python { .api } +def run_hook( + hook: Hook, + file_args: Sequence[str], + color: bool = True +) -> tuple[int, bytes, int]: + """ + Execute a single hook with provided file arguments. + + Parameters: + - hook: Hook instance to execute + - file_args: Files to pass to hook + - color: Enable colored output + + Returns: + - tuple: (return_code, stdout_bytes, duration_ms) + """ +``` + +### Hook Output Formatting + +Functions for formatting and displaying hook execution results. + +```python { .api } +def get_hook_message( + hook: Hook, + return_code: int, + file_count: int, + duration_ms: int, + color: bool = True +) -> str: + """ + Format hook execution result message. + + Parameters: + - hook: Executed hook + - return_code: Hook exit code + - file_count: Number of files processed + - duration_ms: Execution duration + - color: Use colored output + + Returns: + - str: Formatted status message + """ + +def format_hook_output( + hook: Hook, + output: bytes, + use_color: bool = True +) -> str: + """ + Format hook output for display. + + Parameters: + - hook: Hook that produced output + - output: Raw output bytes + - use_color: Enable colored formatting + + Returns: + - str: Formatted output string + """ +``` + +## Hook Creation and Processing + +### Hook Factory Functions + +Functions for creating Hook instances from configuration data. + +```python { .api } +def _hook_from_config( + hook: dict[str, Any], + prefix: Prefix, + manifest: dict[str, Any], + root_config: dict[str, Any] +) -> Hook: + """ + Create Hook instance from configuration and manifest data. + + Parameters: + - hook: Hook configuration from .pre-commit-config.yaml + - prefix: Installation prefix for hook + - manifest: Hook definition from .pre-commit-hooks.yaml + - root_config: Root configuration context + + Returns: + - Hook: Configured hook instance + """ +``` + +### Local Hook Support + +Support for locally defined hooks without external repositories. + +```python { .api } +def local_hook( + hook_dict: dict[str, Any], + prefix: Prefix +) -> Hook: + """ + Create hook from local configuration. + + Parameters: + - hook_dict: Local hook configuration + - prefix: Installation prefix + + Returns: + - Hook: Local hook instance + """ +``` + +## Hook Stages and Types + +### Supported Hook Stages + +```python { .api } +HOOK_TYPES = ( + 'commit-msg', # Commit message validation + 'post-checkout', # After checkout operations + 'post-commit', # After successful commit + 'post-merge', # After merge operations + 'post-rewrite', # After rewrite operations + 'pre-commit', # Before commit (default) + 'pre-merge-commit', # Before merge commits + 'pre-push', # Before push operations + 'pre-rebase', # Before rebase operations + 'prepare-commit-msg' # Commit message preparation +) + +STAGES = (*HOOK_TYPES, 'manual') # All types plus manual execution +``` + +## Usage Examples + +### Working with Hook Instances + +```python +from pre_commit.hook import Hook +from pre_commit.prefix import Prefix + +# Create a prefix for hook installation +prefix = Prefix('/path/to/hook/env') + +# Hook instances are typically created by all_hooks() +# but can be constructed directly for testing +hook = Hook( + src='https://github.com/psf/black', + prefix=prefix, + id='black', + name='black', + entry='black', + language='python', + alias='', + files='\.py$', + exclude='', + types=['python'], + types_or=[], + exclude_types=[], + additional_dependencies=[], + args=[], + always_run=False, + fail_fast=False, + pass_filenames=True, + description='The uncompromising Python code formatter', + language_version='default', + log_file='', + minimum_pre_commit_version='2.9.2', + require_serial=False, + stages=['pre-commit'], + verbose=False +) + +# Check hook properties +print(f"Hook: {hook.name} ({hook.language})") +print(f"Files pattern: {hook.files}") +print(f"Always run: {hook.always_run}") +``` + +### Loading All Hooks from Configuration + +```python +from pre_commit.repository import all_hooks +from pre_commit.clientlib import load_config +from pre_commit.store import Store + +# Load configuration and create store +config = load_config('.pre-commit-config.yaml') +store = Store() + +# Get all configured hooks +hooks = all_hooks(config, store) + +print(f"Found {len(hooks)} hooks:") +for hook in hooks: + print(f" {hook.id}: {hook.name} ({hook.language})") +``` + +### Installing Hook Environments + +```python +from pre_commit.repository import install_hook_envs + +# Install environments for all hooks +install_hook_envs(hooks, store) +print("All hook environments installed") +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/index.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/index.md new file mode 100644 index 000000000..164eee6db --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/index.md @@ -0,0 +1,297 @@ +# Pre-commit + +A framework for managing and maintaining multi-language pre-commit hooks. Pre-commit enables automatic execution of code quality checks, linting, formatting, and testing before commits are made to version control systems, supporting multiple programming languages and providing a plugin-based architecture for extensibility. + +## Package Information + +- **Package Name**: pre-commit +- **Package Type**: pypi +- **Language**: Python +- **Installation**: `pip install pre-commit` + +## Core Imports + +```python +import pre_commit +``` + +For programmatic API usage: + +```python +from pre_commit.main import main +from pre_commit.store import Store +from pre_commit.hook import Hook +from pre_commit.clientlib import load_config, load_manifest, HOOK_TYPES, STAGES +from pre_commit import git +from pre_commit import constants as C +from pre_commit.errors import FatalError, InvalidConfigError, InvalidManifestError +from pre_commit.util import cmd_output, CalledProcessError +from pre_commit.color import format_color, use_color +from pre_commit.output import write_line +``` + +## Basic Usage + +### Command Line Interface + +```bash +# Install git hook scripts +pre-commit install + +# Run hooks on all files +pre-commit run --all-files + +# Run hooks on staged files only +pre-commit run + +# Auto-update hook versions +pre-commit autoupdate + +# Validate configuration +pre-commit validate-config +``` + +### Programmatic Usage + +```python +from pre_commit.main import main +from pre_commit.store import Store +from pre_commit.clientlib import load_config, InvalidConfigError +from pre_commit.errors import FatalError + +# Run pre-commit programmatically +exit_code = main(['run', '--all-files']) + +# Load and validate configuration with error handling +try: + config = load_config('.pre-commit-config.yaml') + print(f"Loaded config with {len(config['repos'])} repositories") +except InvalidConfigError as e: + print(f"Configuration error: {e}") +except FatalError as e: + print(f"Fatal error: {e}") + +# Initialize store for hook management +store = Store() + +# Example: Clone a repository for hook usage +repo_path = store.clone('https://github.com/psf/black', 'main') +print(f"Repository cloned to: {repo_path}") +``` + +## Architecture + +Pre-commit follows a modular architecture built around several key components: + +- **CLI Commands**: Command-line interface providing installation, execution, and management functionality +- **Hook System**: Data structures and execution framework for individual hooks +- **Language Support**: Plugin-based architecture supporting 22+ programming languages +- **Configuration Management**: YAML-based configuration loading and validation +- **Git Integration**: Deep integration with Git workflows and repository operations +- **Repository Management**: Store system for managing hook repositories and environments + +This design enables pre-commit to serve as a comprehensive framework for maintaining code quality across diverse development environments and language ecosystems. + +## Capabilities + +### CLI Commands + +Complete command-line interface for installing, managing, and executing pre-commit hooks. Includes installation commands, hook execution, configuration management, and utility operations. + +```python { .api } +def main(argv: Sequence[str] | None = None) -> int +``` + +[CLI Commands](./cli-commands.md) + +### Configuration Management + +Configuration loading, validation, and schema definitions for .pre-commit-config.yaml and .pre-commit-hooks.yaml files. + +```python { .api } +load_config: functools.partial[dict[str, Any]] +# Partial function for loading .pre-commit-config.yaml files +# Usage: config = load_config(filename) + +load_manifest: functools.partial[list[dict[str, Any]]] +# Partial function for loading .pre-commit-hooks.yaml files +# Usage: hooks = load_manifest(filename) +``` + +[Configuration](./configuration.md) + +### Hook System + +Core data structures and management functions for pre-commit hooks, including hook representation, execution, and environment management. + +```python { .api } +class Hook(NamedTuple): + src: str + prefix: Prefix + id: str + name: str + entry: str + language: str + alias: str + files: str + exclude: str + types: Sequence[str] + types_or: Sequence[str] + exclude_types: Sequence[str] + additional_dependencies: Sequence[str] + args: Sequence[str] + always_run: bool + fail_fast: bool + pass_filenames: bool + description: str + language_version: str + log_file: str + minimum_pre_commit_version: str + require_serial: bool + stages: Sequence[str] + verbose: bool +``` + +[Hooks](./hooks.md) + +### Git Integration + +Git repository utilities and integration functions for working with staged files, repository state, and Git workflow integration. + +```python { .api } +def get_root() -> str +def get_staged_files(cwd: str | None = None) -> list[str] +def get_all_files() -> list[str] +``` + +[Git Integration](./git-integration.md) + +### Language Support + +Multi-language support system with plugin-based architecture for 22+ programming languages including Python, JavaScript, Go, Rust, and more. + +```python { .api } +class Language(Protocol): + def get_default_version(self) -> str + def install_environment(self, prefix: Prefix, version: str, additional_dependencies: Sequence[str]) -> None + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes] +``` + +[Language Support](./language-support.md) + +### Repository Management + +Repository and hook environment management through the Store system, including cloning, caching, and cleanup operations. + +```python { .api } +class Store: + def clone(self, repo: str, ref: str, deps: Sequence[str] = ()) -> str + def make_local(self, deps: Sequence[str]) -> str + def select_all_repos(self) -> list[tuple[str, str, str]] +``` + +[Repository Management](./repository-management.md) + +## Constants and Error Handling + +### Constants + +```python { .api } +CONFIG_FILE: str = '.pre-commit-config.yaml' +MANIFEST_FILE: str = '.pre-commit-hooks.yaml' +LOCAL_REPO_VERSION: str = '1' +DEFAULT: str = 'default' +VERSION: str # Package version string + +# Hook types and stages +HOOK_TYPES: tuple[str, ...] = ( + 'commit-msg', 'post-checkout', 'post-commit', 'post-merge', + 'post-rewrite', 'pre-commit', 'pre-merge-commit', 'pre-push', + 'pre-rebase', 'prepare-commit-msg' +) +STAGES: tuple[str, ...] = (*HOOK_TYPES, 'manual') + +# Repository types +LOCAL: str = 'local' +META: str = 'meta' + +# Color constants +RED: str +GREEN: str +YELLOW: str +TURQUOISE: str +SUBTLE: str +NORMAL: str +COLOR_CHOICES: tuple[str, ...] = ('auto', 'always', 'never') +``` + +### Exceptions + +```python { .api } +class FatalError(RuntimeError): + """Base exception for fatal errors""" + +class InvalidConfigError(FatalError): + """Configuration validation errors""" + +class InvalidManifestError(FatalError): + """Manifest validation errors""" + +class CalledProcessError(RuntimeError): + """Enhanced subprocess error with stdout/stderr capture""" + returncode: int + cmd: tuple[str, ...] + stdout: bytes + stderr: bytes | None +``` + +### Utility Functions + +Core utility functions for process execution, file operations, and resource management. + +```python { .api } +def cmd_output(*cmd: str, **kwargs: Any) -> tuple[int, str, str | None]: + """Execute command and return exit code, stdout, stderr as strings""" + +def cmd_output_b(*cmd: str, check: bool = True, **kwargs: Any) -> tuple[int, bytes, bytes | None]: + """Execute command and return exit code, stdout, stderr as bytes""" + +def make_executable(filename: str) -> None: + """Make file executable""" + +def resource_text(filename: str) -> str: + """Read text from package resource file""" + +def clean_path_on_failure(path: str) -> Generator[None, None, None]: + """Context manager that removes path on exception""" +``` + +### Color Support + +Functions for colored terminal output. + +```python { .api } +def format_color(text: str, color: str, use_color_setting: bool) -> str: + """Format text with ANSI color codes""" + +def use_color(setting: str) -> bool: + """Determine if color should be used based on setting""" + +def add_color_option(parser: argparse.ArgumentParser) -> None: + """Add --color argument to argument parser""" +``` + +### Output Functions + +Terminal output utilities with optional logging support. + +```python { .api } +def write(s: str, stream: IO[bytes] = sys.stdout.buffer) -> None: + """Write string to byte stream""" + +def write_line(s: str | None = None, **kwargs: Any) -> None: + """Write line to output with optional logging""" + +def write_line_b(s: bytes | None = None, stream: IO[bytes] = sys.stdout.buffer, logfile_name: str | None = None) -> None: + """Write line as bytes to output with optional logging""" +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md new file mode 100644 index 000000000..62c55c318 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md @@ -0,0 +1,494 @@ +# Language Support + +Multi-language support system with plugin-based architecture for 22+ programming languages. Pre-commit provides a standardized interface for integrating hooks written in different programming languages, each with their own environment management and execution requirements. + +## Capabilities + +### Language Protocol + +Base protocol that all language implementations must follow. + +```python { .api } +class Language(Protocol): + """ + Protocol defining the interface for language implementations. + + Each supported language must implement this protocol to provide + consistent environment management and hook execution. + """ + + @property + def ENVIRONMENT_DIR(self) -> str | None: + """ + Directory name for language-specific environments. + + Returns None for languages that don't require separate environments. + + Returns: + - str | None: Environment directory name or None + """ + + def get_default_version(self) -> str: + """ + Get the default version identifier for this language. + + Returns: + - str: Default version string (e.g., 'default', 'system') + """ + + def health_check(self, prefix: Prefix, version: str) -> str | None: + """ + Check if the language environment is healthy and functional. + + Parameters: + - prefix: Installation prefix for the environment + - version: Language version to check + + Returns: + - str | None: Error message if unhealthy, None if healthy + """ + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + Install or update the language environment. + + Parameters: + - prefix: Installation prefix for the environment + - version: Language version to install + - additional_dependencies: Extra packages to install + """ + + def in_env(self, prefix: Prefix, version: str) -> ContextManager[None]: + """ + Context manager for executing within the language environment. + + Parameters: + - prefix: Installation prefix for the environment + - version: Language version to use + + Returns: + - ContextManager: Context for environment activation + """ + + def run_hook( + self, + prefix: Prefix, + version: str, + cmd: Sequence[str], + *, + file_args: Sequence[str], + prepend_base_dir: bool, + require_serial: bool, + color: bool + ) -> tuple[int, bytes]: + """ + Execute a hook within the language environment. + + Parameters: + - prefix: Installation prefix for the environment + - version: Language version to use + - cmd: Command and arguments to execute + - file_args: Files to pass to the hook + - prepend_base_dir: Whether to prepend base directory to paths + - require_serial: Whether serial execution is required + - color: Whether to enable colored output + + Returns: + - tuple: (exit_code, output_bytes) + """ +``` + +### Supported Languages + +Complete registry of supported languages with their implementations. + +```python { .api } +languages: dict[str, Language] = { + 'conda': conda, # Conda package manager + 'coursier': coursier, # Scala/Coursier dependency manager + 'dart': dart, # Dart programming language + 'docker': docker, # Docker containers + 'docker_image': docker_image, # Pre-built Docker images + 'dotnet': dotnet, # .NET framework + 'fail': fail, # Always-fail hook for testing + 'golang': golang, # Go programming language + 'haskell': haskell, # Haskell programming language + 'julia': julia, # Julia programming language + 'lua': lua, # Lua programming language + 'node': node, # Node.js/npm + 'perl': perl, # Perl programming language + 'pygrep': pygrep, # Python regex-based hooks + 'python': python, # Python programming language + 'r': r, # R programming language + 'ruby': ruby, # Ruby programming language + 'rust': rust, # Rust programming language + 'script': script, # Shell scripts + 'swift': swift, # Swift programming language + 'system': system # System executables +} +``` + +## Individual Language Implementations + +### Python Language Support + +```python { .api } +class PythonLanguage: + """Python language implementation with virtual environment support.""" + + ENVIRONMENT_DIR = 'py_env' + + def get_default_version(self) -> str: + """Returns 'python3' as default Python version.""" + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + Create virtual environment and install dependencies. + + Creates a Python virtual environment and installs the hook + package along with any additional dependencies. + """ + + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: + """Execute hook within Python virtual environment.""" +``` + +### Node.js Language Support + +```python { .api } +class NodeLanguage: + """Node.js language implementation with npm package management.""" + + ENVIRONMENT_DIR = 'node_env' + + def get_default_version(self) -> str: + """Returns 'node' as default Node.js version.""" + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + Install Node.js packages using npm. + + Sets up npm environment and installs hook package with dependencies. + """ + + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: + """Execute hook with Node.js runtime.""" +``` + +### Go Language Support + +```python { .api } +class GolangLanguage: + """Go language implementation with module support.""" + + ENVIRONMENT_DIR = 'go_env' + + def get_default_version(self) -> str: + """Returns 'go' as default Go version.""" + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + Install Go modules and build binaries. + + Downloads Go modules and builds executable binaries for hook execution. + """ + + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: + """Execute Go binary hook.""" +``` + +### Ruby Language Support + +```python { .api } +class RubyLanguage: + """Ruby language implementation with gem management.""" + + ENVIRONMENT_DIR = 'ruby_env' + + def get_default_version(self) -> str: + """Returns 'ruby' as default Ruby version.""" + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + Install Ruby gems in isolated environment. + + Creates gem environment and installs hook gem with dependencies. + """ + + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: + """Execute hook with Ruby runtime.""" +``` + +### Docker Language Support + +```python { .api } +class DockerLanguage: + """Docker language implementation for containerized hooks.""" + + ENVIRONMENT_DIR = None # No local environment needed + + def get_default_version(self) -> str: + """Returns 'default' for Docker version.""" + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + Build Docker image for hook execution. + + Builds Docker image with hook and its dependencies. + """ + + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: + """Execute hook within Docker container.""" +``` + +### System Language Support + +```python { .api } +class SystemLanguage: + """System language for executing system-installed binaries.""" + + ENVIRONMENT_DIR = None # Uses system executables + + def get_default_version(self) -> str: + """Returns 'system' for system executables.""" + + def install_environment( + self, + prefix: Prefix, + version: str, + additional_dependencies: Sequence[str] + ) -> None: + """ + No installation needed for system executables. + + Validates that required executables are available on system PATH. + """ + + def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: + """Execute system executable directly.""" +``` + +## Language Utilities + +### Language Resolution + +Functions for resolving and working with language implementations. + +```python { .api } +def get_language(language: str) -> Language: + """ + Get language implementation by name. + + Parameters: + - language: Language name + + Returns: + - Language: Language implementation instance + + Raises: + - ValueError: If language is not supported + """ + +def language_version_info(language: str, version: str) -> dict[str, Any]: + """ + Get version information for a language. + + Parameters: + - language: Language name + - version: Version string + + Returns: + - dict: Version information and capabilities + """ +``` + +### Environment Management + +Utilities for managing language environments across hooks. + +```python { .api } +def clean_environment(prefix: Prefix, language: str) -> None: + """ + Clean up language environment files. + + Parameters: + - prefix: Installation prefix + - language: Language name + """ + +def environment_exists(prefix: Prefix, language: str, version: str) -> bool: + """ + Check if language environment is installed. + + Parameters: + - prefix: Installation prefix + - language: Language name + - version: Language version + + Returns: + - bool: True if environment exists + """ +``` + +## Language-Specific Features + +### Python Features + +```python { .api } +def python_venv_info(prefix: Prefix) -> dict[str, str]: + """Get Python virtual environment information.""" + +def install_python_requirements(prefix: Prefix, requirements: list[str]) -> None: + """Install Python packages from requirements list.""" +``` + +### Node.js Features + +```python { .api } +def node_package_info(prefix: Prefix) -> dict[str, Any]: + """Get Node.js package information.""" + +def install_node_packages(prefix: Prefix, packages: list[str]) -> None: + """Install Node.js packages via npm.""" +``` + +### Docker Features + +```python { .api } +def docker_image_exists(image: str) -> bool: + """Check if Docker image exists locally.""" + +def pull_docker_image(image: str) -> None: + """Pull Docker image from registry.""" +``` + +## Usage Examples + +### Working with Language Implementations + +```python +from pre_commit.languages import languages +from pre_commit.prefix import Prefix + +# Get Python language implementation +python_lang = languages['python'] +print(f"Python environment dir: {python_lang.ENVIRONMENT_DIR}") +print(f"Default version: {python_lang.get_default_version()}") + +# Create prefix for hook installation +prefix = Prefix('/tmp/hook-env') + +# Install Python environment with additional dependencies +python_lang.install_environment( + prefix=prefix, + version='python3.9', + additional_dependencies=['black', 'flake8'] +) + +# Check environment health +health_result = python_lang.health_check(prefix, 'python3.9') +if health_result is None: + print("Python environment is healthy") +else: + print(f"Environment issue: {health_result}") +``` + +### Executing Hooks with Language Support + +```python +from pre_commit.languages import languages + +# Get language implementation +node_lang = languages['node'] + +# Execute hook within Node.js environment +with node_lang.in_env(prefix, 'node'): + exit_code, output = node_lang.run_hook( + prefix=prefix, + version='node', + cmd=['eslint', '--fix'], + file_args=['src/app.js', 'src/utils.js'], + prepend_base_dir=True, + require_serial=False, + color=True + ) + +print(f"Hook exit code: {exit_code}") +if output: + print(f"Hook output: {output.decode()}") +``` + +### Language Environment Management + +```python +from pre_commit.languages import get_language, environment_exists + +# Work with Go language +go_lang = get_language('golang') + +# Check if environment exists +if environment_exists(prefix, 'golang', 'go1.19'): + print("Go environment already installed") +else: + print("Installing Go environment...") + go_lang.install_environment(prefix, 'go1.19', []) + +# Get language version info +version_info = language_version_info('golang', 'go1.19') +print(f"Go version info: {version_info}") +``` + +### Multi-Language Hook Processing + +```python +from pre_commit.languages import languages + +# Process hooks for different languages +hook_configs = [ + {'language': 'python', 'version': 'python3.9', 'deps': ['black']}, + {'language': 'node', 'version': 'node16', 'deps': ['eslint']}, + {'language': 'golang', 'version': 'go1.19', 'deps': []}, +] + +for config in hook_configs: + lang_impl = languages[config['language']] + + # Install environment + lang_impl.install_environment( + prefix=prefix, + version=config['version'], + additional_dependencies=config['deps'] + ) + + print(f"Installed {config['language']} environment") +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md new file mode 100644 index 000000000..f4944d82d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md @@ -0,0 +1,425 @@ +# Repository Management + +Repository and hook environment management through the Store system. The Store provides centralized management of hook repositories, environment caching, and cleanup operations for efficient hook execution and storage. + +## Capabilities + +### Store Class + +Central repository and environment management system that handles cloning, caching, and lifecycle management of hook repositories. + +```python { .api } +class Store: + """ + Centralized store for managing hook repositories and environments. + + Provides caching, cloning, and cleanup functionality for hook repositories + and their associated environments. Manages the complete lifecycle from + initial repository cloning through environment installation to cleanup. + """ + + def __init__(self, directory: str | None = None) -> None: + """ + Initialize store with optional custom directory. + + Parameters: + - directory: Custom store directory path (uses default if None) + """ + + def clone(self, repo: str, ref: str, deps: Sequence[str] = ()) -> str: + """ + Clone repository and return installation path. + + Clones the specified repository at the given reference and prepares + it for hook installation. Handles caching to avoid duplicate clones. + + Parameters: + - repo: Repository URL or local path + - ref: Git reference (tag, commit, branch) + - deps: Additional dependencies for environment + + Returns: + - str: Path to cloned repository installation + """ + + def make_local(self, deps: Sequence[str]) -> str: + """ + Create local repository installation for local hooks. + + Sets up environment for locally defined hooks without external repository. + + Parameters: + - deps: Dependencies for local hook environment + + Returns: + - str: Path to local installation + """ + + def mark_config_used(self, path: str) -> None: + """ + Mark configuration as recently used for garbage collection. + + Parameters: + - path: Configuration file path to mark as used + """ + + def select_all_configs(self) -> list[str]: + """ + Get all configuration files tracked by the store. + + Returns: + - list: Paths to all tracked configuration files + """ + + def delete_configs(self, configs: list[str]) -> None: + """ + Delete specified configuration files from tracking. + + Parameters: + - configs: List of configuration paths to delete + """ + + def select_all_repos(self) -> list[tuple[str, str, str]]: + """ + Get all repositories managed by the store. + + Returns: + - list: Tuples of (repo_name, ref, path) for each repository + """ + + def delete_repo(self, db_repo_name: str, ref: str, path: str) -> None: + """ + Delete repository from store and filesystem. + + Parameters: + - db_repo_name: Database repository name + - ref: Git reference + - path: Repository path to delete + """ + + @staticmethod + def get_default_directory() -> str: + """ + Get the default store directory path. + + Returns: + - str: Default store directory (typically ~/.cache/pre-commit) + """ +``` + +### Repository Operations + +High-level functions for working with repositories through the store system. + +```python { .api } +def all_hooks(root_config: dict[str, Any], store: Store) -> tuple[Hook, ...]: + """ + Extract all configured hooks from configuration using store. + + Processes the complete configuration file, clones required repositories + through the store, and creates Hook instances for all defined hooks. + + Parameters: + - root_config: Loaded configuration dictionary + - store: Store instance for repository management + + Returns: + - tuple: All configured Hook instances + """ + +def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None: + """ + Install environments for all provided hooks using store. + + Coordinates with the store to ensure all hook environments are properly + installed and configured with their dependencies. + + Parameters: + - hooks: Sequence of hooks requiring environment installation + - store: Store instance for environment management + """ +``` + +### Store Database Management + +Internal database operations for tracking repositories and configurations. + +```python { .api } +def _get_store_db(store_dir: str) -> sqlite3.Connection: + """ + Get database connection for store operations. + + Parameters: + - store_dir: Store directory path + + Returns: + - Connection: SQLite database connection + """ + +def _init_store_db(db: sqlite3.Connection) -> None: + """ + Initialize store database with required tables. + + Parameters: + - db: Database connection to initialize + """ +``` + +## Store Configuration + +### Directory Structure + +The store maintains a structured directory layout for efficient organization: + +``` +~/.cache/pre-commit/ # Default store directory +├── db.db # SQLite database for tracking +├── repos/ # Cloned repositories +│ ├── / # Individual repository directories +│ │ ├── .git/ # Git repository data +│ │ └── # Hook implementation files +│ └── ... +└── / # Language-specific environments + ├── py_env-/ # Python virtual environments + ├── node_env-/ # Node.js environments + └── ... +``` + +### Store Initialization + +```python { .api } +def initialize_store(directory: str | None = None) -> Store: + """ + Initialize a new store instance. + + Parameters: + - directory: Custom directory (uses default if None) + + Returns: + - Store: Initialized store instance + """ +``` + +## Repository Caching + +### Cache Management + +Functions for managing repository and environment caches. + +```python { .api } +def clean_unused_repos(store: Store, configs_in_use: set[str]) -> None: + """ + Clean repositories not referenced by active configurations. + + Parameters: + - store: Store instance to clean + - configs_in_use: Set of configuration files still in use + """ + +def get_cache_stats(store: Store) -> dict[str, Any]: + """ + Get statistics about store cache usage. + + Parameters: + - store: Store instance to analyze + + Returns: + - dict: Cache statistics including size, file counts, etc. + """ +``` + +### Garbage Collection + +```python { .api } +def garbage_collect_repos(store: Store) -> int: + """ + Perform garbage collection on unused repositories. + + Removes repositories and environments that are no longer referenced + by any active configurations. + + Parameters: + - store: Store instance to garbage collect + + Returns: + - int: Number of items removed + """ +``` + +## Environment Management + +### Environment Installation + +```python { .api } +def ensure_environment( + hook: Hook, + store: Store, + language_version: str +) -> None: + """ + Ensure hook environment is installed and ready. + + Parameters: + - hook: Hook requiring environment + - store: Store instance for management + - language_version: Specific language version to use + """ +``` + +### Environment Health Checks + +```python { .api } +def check_environment_health( + prefix: Prefix, + language: str, + version: str +) -> str | None: + """ + Check if environment is healthy and functional. + + Parameters: + - prefix: Environment installation prefix + - language: Programming language + - version: Language version + + Returns: + - str | None: Error message if unhealthy, None if healthy + """ +``` + +## Usage Examples + +### Basic Store Operations + +```python +from pre_commit.store import Store +from pre_commit.clientlib import load_config +from pre_commit.repository import all_hooks, install_hook_envs + +# Initialize store (uses default directory) +store = Store() + +# Or initialize with custom directory +custom_store = Store('/path/to/custom/store') + +# Load configuration and get all hooks +config = load_config('.pre-commit-config.yaml') +hooks = all_hooks(config, store) + +print(f"Found {len(hooks)} hooks from {len(config['repos'])} repositories") + +# Install environments for all hooks +install_hook_envs(hooks, store) +print("All hook environments installed") +``` + +### Repository Cloning and Management + +```python +from pre_commit.store import Store + +store = Store() + +# Clone specific repository +repo_path = store.clone( + repo='https://github.com/psf/black', + ref='22.3.0', + deps=['black[jupyter]'] +) +print(f"Repository cloned to: {repo_path}") + +# Create local environment +local_path = store.make_local(deps=['flake8', 'mypy']) +print(f"Local environment created at: {local_path}") + +# Mark configuration as used (for garbage collection) +store.mark_config_used('.pre-commit-config.yaml') +``` + +### Store Maintenance + +```python +from pre_commit.store import Store + +store = Store() + +# Get all repositories managed by store +repos = store.select_all_repos() +print(f"Store manages {len(repos)} repositories:") +for repo_name, ref, path in repos: + print(f" {repo_name}@{ref} -> {path}") + +# Get all tracked configurations +configs = store.select_all_configs() +print(f"Tracking {len(configs)} configurations:") +for config_path in configs: + print(f" {config_path}") + +# Clean up unused configurations +# (In practice, you'd determine which are actually unused) +# store.delete_configs(['/path/to/unused/config.yaml']) +``` + +### Cache Statistics and Cleanup + +```python +from pre_commit.store import Store, get_cache_stats, garbage_collect_repos + +store = Store() + +# Get cache statistics +stats = get_cache_stats(store) +print(f"Cache statistics:") +print(f" Total repositories: {stats.get('repo_count', 0)}") +print(f" Total size: {stats.get('total_size', 0)} bytes") +print(f" Environments: {stats.get('env_count', 0)}") + +# Perform garbage collection +removed_count = garbage_collect_repos(store) +print(f"Garbage collection removed {removed_count} unused items") +``` + +### Working with Store Directory + +```python +from pre_commit.store import Store + +# Get default store directory +default_dir = Store.get_default_directory() +print(f"Default store directory: {default_dir}") + +# Initialize store with explicit directory +store = Store(directory='/tmp/pre-commit-store') + +# Verify store is using correct directory +# (Store doesn't expose directory directly, but you can check filesystem) +import os +if os.path.exists('/tmp/pre-commit-store'): + print("Custom store directory created successfully") +``` + +### Advanced Repository Operations + +```python +from pre_commit.store import Store + +store = Store() + +# Work with multiple repositories +repositories = [ + ('https://github.com/psf/black', '22.3.0'), + ('https://github.com/pycqa/flake8', '4.0.1'), + ('https://github.com/pre-commit/mirrors-mypy', 'v0.950') +] + +repo_paths = [] +for repo_url, ref in repositories: + path = store.clone(repo=repo_url, ref=ref) + repo_paths.append(path) + print(f"Cloned {repo_url}@{ref} to {path}") + +print(f"Successfully cloned {len(repo_paths)} repositories") + +# Clean up specific repository if needed +# store.delete_repo('repo-name', 'ref', '/path/to/repo') +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/tile.json b/.tessl/tiles/tessl/pypi-pre-commit/tile.json new file mode 100644 index 000000000..e4f228093 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pre-commit/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-pre-commit", + "version": "4.3.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/pre-commit@4.3.0", + "summary": "A framework for managing and maintaining multi-language pre-commit hooks." +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/assertions.md b/.tessl/tiles/tessl/pypi-pytest/docs/assertions.md new file mode 100644 index 000000000..77bf3546a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/assertions.md @@ -0,0 +1,280 @@ +# Assertions and Exception Handling + +Context managers and utilities for asserting exceptions, warnings, and performing approximate numerical comparisons. These tools provide comprehensive testing capabilities for exception handling, warning validation, and floating-point comparisons. + +## Capabilities + +### Exception Assertion + +Context manager for asserting that specific exceptions are raised during code execution. + +```python { .api } +def raises(expected_exception, *, match=None, check=None): + """ + Context manager for asserting exceptions are raised. + + Parameters: + - expected_exception: Exception class or tuple of exception classes + - match: Regex pattern that exception message should match + - check: Callable to perform additional validation on exception + + Returns: + RaisesExc context manager or ExceptionInfo if used as function decorator + """ + +class RaisesExc: + """Context manager for exception assertion with type checking.""" + + def matches(self, expected_exception) -> bool: + """Check if captured exception matches expected type.""" + + def __enter__(self) -> ExceptionInfo: + """Enter context manager.""" + + def __exit__(self, exc_type, exc_val, exc_tb) -> bool: + """Exit context manager and validate exception.""" +``` + +**Usage Example:** + +```python +import pytest +import re + +# Basic exception assertion +def test_division_by_zero(): + with pytest.raises(ZeroDivisionError): + 1 / 0 + +# With message matching +def test_invalid_input(): + with pytest.raises(ValueError, match=r"invalid.*input"): + process_input("invalid data") + +# Multiple exception types +def test_file_operations(): + with pytest.raises((FileNotFoundError, PermissionError)): + open("/nonexistent/file", "r") + +# Custom validation +def test_custom_exception(): + with pytest.raises(CustomError) as exc_info: + raise_custom_error() + + assert exc_info.value.error_code == 123 + assert "custom message" in str(exc_info.value) +``` + +### Exception Group Assertion + +Context manager for asserting ExceptionGroup exceptions (Python 3.11+). + +```python { .api } +class RaisesGroup: + """Context manager for ExceptionGroup assertion.""" + + def matches(self, expected_exceptions) -> bool: + """Check if exception group contains expected exceptions.""" + + def expected_type(self) -> type: + """Get the expected exception group type.""" +``` + +### Exception Information + +Provides detailed information about caught exceptions with advanced introspection capabilities. + +```python { .api } +class ExceptionInfo: + """Information about caught exceptions.""" + + type: type # Exception type + value: Exception # Exception instance + tb: types.TracebackType # Traceback object + typename: str # Exception type name as string + traceback: Traceback # pytest's enhanced traceback + + def exconly(self, tryshort: bool = False) -> str: + """Return exception as string without traceback.""" + + def errisinstance(self, exc: type | tuple[type, ...]) -> bool: + """Check if exception is instance of given type(s).""" + + def match(self, regexp: str | re.Pattern) -> bool: + """Check if exception message matches regex pattern.""" + + def group_contains(self, expected_exception, *, match=None, depth: int = 1) -> bool: + """Check if exception group contains specific exception type.""" +``` + +### Warning Assertion + +Context manager for asserting that specific warnings are issued during code execution. + +```python { .api } +def warns(expected_warning, *, match=None): + """ + Context manager for asserting warnings are issued. + + Parameters: + - expected_warning: Warning class or tuple of warning classes + - match: Regex pattern that warning message should match + + Returns: + WarningsRecorder context manager + """ + +class WarningsRecorder: + """Records warnings during test execution.""" + + def pop(self, cls=Warning): + """Remove and return last warning of given class.""" + + def clear(self): + """Clear all recorded warnings.""" + + @property + def list(self) -> list: + """List of all recorded warnings.""" +``` + +**Usage Example:** + +```python +import pytest +import warnings + +def test_deprecation_warning(): + with pytest.warns(DeprecationWarning): + warnings.warn("This is deprecated", DeprecationWarning) + +def test_warning_message(): + with pytest.warns(UserWarning, match=r"custom.*warning"): + warnings.warn("This is a custom warning", UserWarning) + +def test_multiple_warnings(): + with pytest.warns() as record: + warnings.warn("Warning 1", UserWarning) + warnings.warn("Warning 2", FutureWarning) + + assert len(record) == 2 + assert record[0].category == UserWarning + assert record[1].category == FutureWarning +``` + +### Deprecation Call Assertion + +Context manager for asserting that deprecation warnings are issued during function calls. + +```python { .api } +def deprecated_call(func=None, *args, **kwargs): + """ + Context manager for asserting deprecation warnings. + + Parameters: + - func: Function to call (optional, can be used as context manager) + - args: Arguments to pass to function + - kwargs: Keyword arguments to pass to function + + Returns: + WarningsRecorder if used as context manager, function result if used as function call + """ +``` + +**Usage Example:** + +```python +import pytest + +# As context manager +def test_deprecated_usage(): + with pytest.deprecated_call(): + deprecated_function() + +# As function call +def test_deprecated_function(): + result = pytest.deprecated_call(deprecated_function, arg1, arg2) + assert result == expected_value +``` + +### Approximate Comparisons + +Create approximate comparison objects for floating-point numbers, sequences, and mappings. + +```python { .api } +def approx(expected, rel=None, abs=None, nan_ok: bool = False): + """ + Create approximate comparison objects for floating-point numbers. + + Parameters: + - expected: Expected value (number, sequence, or mapping) + - rel: Relative tolerance (default: 1e-6) + - abs: Absolute tolerance (default: 1e-12) + - nan_ok: Whether NaN values should be considered equal + + Returns: + ApproxBase object (ApproxScalar, ApproxSequence, ApproxMapping, or ApproxNumpy) + """ +``` + +**Usage Example:** + +```python +import pytest +import math + +def test_float_comparison(): + assert 0.1 + 0.2 == pytest.approx(0.3) + assert math.pi == pytest.approx(3.14159, rel=1e-4) + +def test_sequence_comparison(): + assert [0.1 + 0.2, 0.2 + 0.3] == pytest.approx([0.3, 0.5]) + +def test_dict_comparison(): + result = {"a": 0.1 + 0.2, "b": 0.2 + 0.3} + expected = {"a": 0.3, "b": 0.5} + assert result == pytest.approx(expected) + +def test_custom_tolerance(): + assert 1.0001 == pytest.approx(1.0, abs=1e-3) + assert 1.01 == pytest.approx(1.0, rel=0.02) + +def test_nan_handling(): + assert float('nan') == pytest.approx(float('nan'), nan_ok=True) +``` + +## Types + +```python { .api } +from typing import Any, Callable, Pattern, Union +import re +import types + +# Type definitions for exception handling +ExceptionClass = type[Exception] +ExceptionTuple = tuple[ExceptionClass, ...] +ExceptionSpec = Union[ExceptionClass, ExceptionTuple] + +MatchPattern = Union[str, Pattern[str]] +CheckFunction = Callable[[Exception], bool] + +class ApproxBase: + """Base class for approximate comparison objects.""" + pass + +class ApproxScalar(ApproxBase): + """Approximate comparison for scalar values.""" + pass + +class ApproxSequence(ApproxBase): + """Approximate comparison for sequences.""" + pass + +class ApproxMapping(ApproxBase): + """Approximate comparison for mappings.""" + pass + +class ApproxNumpy(ApproxBase): + """Approximate comparison for numpy arrays.""" + pass +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/configuration.md b/.tessl/tiles/tessl/pypi-pytest/docs/configuration.md new file mode 100644 index 000000000..de827a53b --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/configuration.md @@ -0,0 +1,460 @@ +# Configuration and Plugin System + +Central configuration management and plugin architecture enabling extensive customization through hooks, configuration files, command-line options, and the pluggy-based plugin system. + +## Capabilities + +### Configuration Management + +Central configuration object providing access to all pytest settings and options. + +```python { .api } +class Config: + """Central configuration object with access to pluginmanager and hooks.""" + + # Core attributes + pluginmanager: PytestPluginManager # Plugin manager instance + invocation_params: InvocationParams # Command-line invocation parameters + option: argparse.Namespace # Parsed command-line options + rootpath: Path # Root directory path + inipath: Path | None # Path to configuration file + + def getini(self, name: str) -> Any: + """ + Get configuration value from ini file. + + Parameters: + - name: Configuration option name + + Returns: + Configuration value + """ + + def getoption(self, name: str, default=None, skip: bool = False) -> Any: + """ + Get command-line option value. + + Parameters: + - name: Option name + - default: Default value if option not set + - skip: Whether to skip if option not found + + Returns: + Option value + """ + + def addinivalue_line(self, name: str, line: str) -> None: + """ + Add line to multi-line ini configuration value. + + Parameters: + - name: Configuration option name + - line: Line to add + """ + + def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: + """Issue warning during configuration time.""" +``` + +### Plugin Management + +Plugin manager extending pluggy with pytest-specific functionality for loading and managing plugins. + +```python { .api } +class PytestPluginManager: + """Extends pluggy.PluginManager with pytest-specific functionality.""" + + def register(self, plugin, name: str | None = None) -> None: + """ + Register a plugin instance. + + Parameters: + - plugin: Plugin instance + - name: Optional plugin name + """ + + def unregister(self, plugin=None, name: str | None = None) -> Any: + """ + Unregister a plugin instance. + + Parameters: + - plugin: Plugin instance to unregister + - name: Plugin name to unregister + + Returns: + Unregistered plugin + """ + + def get_plugin(self, name: str): + """Get plugin by name.""" + + def is_registered(self, plugin) -> bool: + """Check if plugin is registered.""" + + def list_plugin_distinfo(self) -> list[tuple[Any, DistInfo]]: + """List all plugin distribution info.""" + + def list_name_plugin(self) -> list[tuple[str, Any]]: + """List all plugins with their names.""" + + def get_canonical_name(self, plugin) -> str: + """Get canonical name for plugin.""" + + def load_setuptools_entrypoints(self, group: str, names: str | None = None) -> int: + """Load plugins from setuptools entry points.""" + + def consider_env(self, name: str, value: str | None = None) -> None: + """Consider environment variable for plugin loading.""" + + def consider_preparse(self, args: list[str], exclude_only: bool = False) -> None: + """Consider command-line arguments for plugin loading.""" + + def consider_conftest(self, conftestmodule) -> None: + """Register hooks from conftest module.""" +``` + +### Hook System + +Decorators for marking functions as hook implementations and specifications. + +```python { .api } +def hookimpl(**kwargs): + """ + Mark function as hook implementation. + + Parameters: + - tryfirst: Execute this hook first + - trylast: Execute this hook last + - hookwrapper: Hook wrapper (can yield) + - optionalhook: Hook is optional + - specname: Name of hook specification + + Returns: + HookimplMarker decorator + """ + +def hookspec(**kwargs): + """ + Mark function as hook specification. + + Parameters: + - firstresult: Return first non-None result + - historic: Historic hook (replay for late registrations) + - warn_on_impl: Issue warnings for implementations + + Returns: + HookspecMarker decorator + """ +``` + +**Usage Example:** + +```python +import pytest + +# Plugin implementation +class MyPlugin: + @pytest.hookimpl(tryfirst=True) + def pytest_configure(self, config): + print("Configuring my plugin") + + @pytest.hookimpl + def pytest_collection_modifyitems(self, config, items): + # Modify collected test items + pass + + @pytest.hookimpl(hookwrapper=True) + def pytest_runtest_setup(self, item): + print(f"Setting up {item.name}") + outcome = yield + print(f"Setup complete for {item.name}") + +# Register plugin +def pytest_configure(config): + config.pluginmanager.register(MyPlugin(), "myplugin") +``` + +### Argument Parsing + +Command-line argument parser for pytest with support for options and ini values. + +```python { .api } +class Parser: + """Command-line argument parser for pytest.""" + + def addoption( + self, + *names, + action=None, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=None, + help=None, + metavar=None, + dest=None, + **kwargs + ) -> None: + """Add command-line option.""" + + def getgroup(self, name: str, description: str = "") -> OptionGroup: + """Get or create option group.""" + + def addini( + self, + name: str, + help: str, + type: str | None = None, + default=None, + **kwargs + ) -> None: + """Add ini file configuration option.""" + +class OptionGroup: + """Groups related command-line options.""" + + def addoption(self, *names, **kwargs) -> None: + """Add option to this group.""" + + def _addoption(self, *names, **kwargs) -> None: + """Internal method to add option.""" +``` + +**Usage Example:** + +```python +# In conftest.py or plugin +def pytest_addoption(parser): + parser.addoption( + "--myopt", + action="store", + default="default", + help="My custom option" + ) + + group = parser.getgroup("mygroup", "My custom options") + group.addoption( + "--verbose-mode", + action="store_true", + help="Enable verbose mode" + ) + + parser.addini( + "my_setting", + help="My configuration setting", + type="string", + default="default_value" + ) + +def pytest_configure(config): + my_opt = config.getoption("myopt") + my_setting = config.getini("my_setting") +``` + +### Exit Codes + +Enumeration of pytest exit codes for different execution outcomes. + +```python { .api } +from enum import IntEnum + +class ExitCode(IntEnum): + """Integer enum for pytest exit codes.""" + + OK = 0 # All tests passed + TESTS_FAILED = 1 # One or more tests failed + INTERRUPTED = 2 # Test run interrupted + INTERNAL_ERROR = 3 # Internal pytest error + USAGE_ERROR = 4 # Command-line usage error + NO_TESTS_RAN = 5 # No tests found/collected +``` + +### Configuration Exceptions + +```python { .api } +class UsageError(Exception): + """ + Raised for command-line usage errors. + + This exception is raised when: + - Invalid command-line arguments are provided + - Required options are missing + - Option values are invalid + """ +``` + +## Entry Points + +Main entry points for running pytest programmatically. + +```python { .api } +def main(args=None, plugins=None) -> int: + """ + Main entry point for running pytest programmatically. + + Parameters: + - args: Command-line arguments (defaults to sys.argv) + - plugins: List of plugin objects to register + + Returns: + Exit code (ExitCode enum value) + """ + +def console_main() -> int: + """ + Console script entry point. + Called by pytest command-line script. + + Returns: + Exit code + """ + +# Module-level entry point +import pytest.config.cmdline + +def cmdline.main() -> int: + """Command line interface entry point.""" +``` + +## Configuration Files + +pytest supports multiple configuration file formats: + +```python +# pytest.ini +[tool:pytest] +addopts = -v --tb=short +testpaths = tests +python_files = test_*.py *_test.py +python_classes = Test* +python_functions = test_* +markers = + slow: marks tests as slow + integration: marks tests as integration tests + +# pyproject.toml +[tool.pytest.ini_options] +addopts = "-v --tb=short" +testpaths = ["tests"] +python_files = ["test_*.py", "*_test.py"] + +# setup.cfg +[tool:pytest] +addopts = -v --tb=short +testpaths = tests +``` + +## Hook Specifications + +Comprehensive hook system for extending pytest functionality at every stage: + +```python { .api } +# Configuration and startup hooks +def pytest_configure(config: Config) -> None: + """Called after command line options are parsed and plugins loaded.""" + +def pytest_unconfigure(config: Config) -> None: + """Called before test process is exited.""" + +def pytest_sessionstart(session: Session) -> None: + """Called after Session object has been created.""" + +def pytest_sessionfinish(session: Session, exitstatus: int) -> None: + """Called after whole test run finished.""" + +# Collection hooks +def pytest_collect_file(parent: Collector, path: Path) -> Collector | None: + """Create a collector for the given path.""" + +def pytest_collection_modifyitems(config: Config, items: list[Item]) -> None: + """Called after collection is completed to modify or re-order items.""" + +def pytest_generate_tests(metafunc: Metafunc) -> None: + """Generate (parametrize) tests for the given function.""" + +# Test execution hooks +def pytest_runtest_setup(item: Item) -> None: + """Called to perform the setup phase for a test item.""" + +def pytest_runtest_call(item: Item) -> None: + """Called to run the test for test item.""" + +def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: + """Called to perform the teardown phase for a test item.""" + +def pytest_runtest_makereport(item: Item, call: CallInfo) -> TestReport | None: + """Called to create a TestReport for each phase.""" + +# Fixture hooks +def pytest_fixture_setup(fixturedef: FixtureDef, request: FixtureRequest) -> object: + """Perform fixture setup execution.""" + +def pytest_fixture_post_finalizer(fixturedef: FixtureDef, request: FixtureRequest) -> None: + """Called after fixture teardown.""" + +# Reporting hooks +def pytest_report_teststatus(report: TestReport, config: Config) -> tuple[str, str, str] | None: + """Return result-category, shortletter and verbose word for reporting.""" + +def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus: int, config: Config) -> None: + """Add sections to the terminal summary reporting.""" + +# Warning and error hooks +def pytest_warning_recorded(warning_message, when: str, nodeid: str, location: tuple[str, int, str]) -> None: + """Process a warning captured by the internal pytest warnings plugin.""" + +def pytest_internalerror(excrepr: ExceptionRepr, excinfo: ExceptionInfo) -> bool: + """Called for internal errors.""" +``` + +## Plugin Development + +```python +# conftest.py - Automatic plugin loading +def pytest_configure(config): + """Called after command line options are parsed.""" + +def pytest_collection_modifyitems(config, items): + """Called after collection is completed.""" + +def pytest_runtest_setup(item): + """Called before each test runs.""" + +# External plugin package +from setuptools import setup + +setup( + name="pytest-myplugin", + entry_points={ + "pytest11": [ + "myplugin = myplugin.plugin", + ], + }, +) +``` + +## Types + +```python { .api } +from typing import Any +from pathlib import Path + +class ExceptionRepr: + """Representation of an exception for reporting purposes.""" + + def toterminal(self, tw: TerminalWriter) -> None: + """Write exception representation to terminal.""" + + def __str__(self) -> str: + """String representation of exception.""" + +class InvocationParams: + """Parameters used during pytest invocation.""" + + # Attributes + args: tuple[str, ...] # Command line arguments + plugins: tuple[str, ...] | None # Plugin names + dir: Path # Working directory +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/fixtures.md b/.tessl/tiles/tessl/pypi-pytest/docs/fixtures.md new file mode 100644 index 000000000..369f5ec05 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/fixtures.md @@ -0,0 +1,311 @@ +# Fixtures and Dependency Injection + +Comprehensive fixture system for managing test dependencies, setup, and teardown with support for multiple scopes, parametrization, and automatic resolution. The fixture system provides a powerful dependency injection framework that eliminates the need for traditional setUp/tearDown methods. + +## Capabilities + +### Fixture Declaration + +Decorator for marking functions as fixture factories that provide reusable test components. + +```python { .api } +def fixture( + fixture_function=None, + *, + scope: str = "function", + params=None, + autouse: bool = False, + ids=None, + name: str | None = None +): + """ + Mark function as fixture factory. + + Parameters: + - fixture_function: Function to mark as fixture (when used as decorator) + - scope: Fixture scope ("function", "class", "module", "package", "session") + - params: List of parameters for parametrized fixtures + - autouse: Whether fixture runs automatically for all tests + - ids: List of test IDs for parametrized fixtures + - name: Alternative name for the fixture + + Returns: + FixtureFunctionMarker or FixtureFunctionDefinition + """ +``` + +**Usage Example:** + +```python +import pytest +import tempfile +import os + +# Basic fixture +@pytest.fixture +def sample_data(): + return [1, 2, 3, 4, 5] + +# Fixture with teardown +@pytest.fixture +def temp_file(): + fd, path = tempfile.mkstemp() + yield path # This is where the test runs + os.close(fd) + os.unlink(path) + +# Scoped fixture +@pytest.fixture(scope="module") +def database_connection(): + connection = create_db_connection() + yield connection + connection.close() + +# Parametrized fixture +@pytest.fixture(params=[1, 2, 3]) +def number(request): + return request.param + +# Autouse fixture +@pytest.fixture(autouse=True) +def setup_test_environment(): + setup_environment() + yield + cleanup_environment() + +# Named fixture +@pytest.fixture(name="data") +def sample_data_fixture(): + return {"key": "value"} + +def test_with_fixtures(sample_data, temp_file, number, data): + assert len(sample_data) == 5 + assert os.path.exists(temp_file) + assert number in [1, 2, 3] + assert data["key"] == "value" +``` + +### Legacy Fixture Support + +```python { .api } +def yield_fixture(*args, **kwargs): + """ + Legacy alias for fixture decorator (deprecated). + Use pytest.fixture instead. + """ +``` + +### Fixture Request + +Object passed to fixture functions providing access to the requesting test context and fixture management capabilities. + +```python { .api } +class FixtureRequest: + """Request object passed to fixture functions.""" + + # Core attributes + node: Node # Test node that requested the fixture + scope: str # Fixture scope + fixturename: str # Name of the fixture + config: Config # pytest configuration object + function: Callable | None # Test function (if applicable) + cls: type | None # Test class (if applicable) + instance: Any | None # Test class instance (if applicable) + module: Any | None # Test module + param: Any # Current parameter (for parametrized fixtures) + keywords: dict # Test keywords/markers + + def getfixturevalue(self, argname: str) -> Any: + """ + Get value of another fixture by name. + + Parameters: + - argname: Name of fixture to retrieve + + Returns: + Fixture value + """ + + def applymarker(self, marker) -> None: + """ + Apply marker to the test node. + + Parameters: + - marker: Marker to apply + """ + + def raiseerror(self, msg: str | None) -> None: + """ + Raise FixtureLookupError with given message. + + Parameters: + - msg: Error message + """ + + @property + def fspath(self): + """Legacy property for file path (use node.path instead).""" +``` + +**Usage Example:** + +```python +import pytest + +@pytest.fixture +def user_data(request): + # Access test function name + test_name = request.node.name + + # Get configuration + config_value = request.config.getini("test_setting") + + # Access other fixtures + database = request.getfixturevalue("database_connection") + + # Create user based on test context + if "admin" in test_name: + return create_admin_user(database) + else: + return create_regular_user(database) + +@pytest.fixture(params=["sqlite", "postgresql"]) +def database_type(request): + return request.param + +@pytest.fixture +def configured_database(request, database_type): + # Access parameter from parametrized dependency + db_type = database_type + config = request.config + + return setup_database(db_type, config) +``` + +### Fixture Definition + +Container for fixture definitions with metadata and execution logic. + +```python { .api } +class FixtureDef: + """Container for fixture definitions.""" + + # Core attributes + argname: str # Fixture argument name + func: Callable # Fixture function + scope: str # Fixture scope + params: list | None # Parameters for parametrized fixtures + autouse: bool # Whether fixture is automatically used + ids: list | None # Parameter IDs + + # Execution methods (internal use) + def execute(self, request: FixtureRequest): ... + def finish(self, request: FixtureRequest): ... +``` + +### Fixture Exceptions + +Exception raised when fixture cannot be found or resolved. + +```python { .api } +class FixtureLookupError(Exception): + """ + Raised when fixture cannot be found or resolved. + + This exception is raised when: + - A fixture name is not found + - Circular fixture dependencies are detected + - Fixture scope conflicts occur + """ +``` + +## Fixture Scopes + +```python { .api } +# Fixture scope hierarchy (from narrowest to broadest) +FIXTURE_SCOPES = [ + "function", # Default: new instance per test function + "class", # One instance per test class + "module", # One instance per test module + "package", # One instance per test package + "session" # One instance per test session +] +``` + +## Built-in Fixtures + +pytest provides many built-in fixtures automatically available in all tests: + +```python { .api } +# Core built-in fixtures (examples) +def test_with_builtins( + request, # FixtureRequest object + config, # Config object + monkeypatch, # MonkeyPatch object + capfd, # CaptureFixture for file descriptors + capsys, # CaptureFixture for sys.stdout/stderr + caplog, # LogCaptureFixture + tmp_path, # pathlib.Path to temporary directory + tmp_path_factory, # TempPathFactory + pytester, # Pytester for testing pytest plugins + recwarn # WarningsRecorder +): + pass +``` + +## Advanced Fixture Patterns + +### Fixture Dependencies + +```python +@pytest.fixture +def database(): + return setup_database() + +@pytest.fixture +def user(database): + return create_user(database) + +@pytest.fixture +def admin_user(database): + return create_admin_user(database) + +def test_user_permissions(user, admin_user): + assert not user.is_admin + assert admin_user.is_admin +``` + +### Conditional Fixtures + +```python +@pytest.fixture +def storage_backend(request): + if request.config.getoption("--use-redis"): + return RedisStorage() + else: + return MemoryStorage() +``` + +### Factory Fixtures + +```python +@pytest.fixture +def user_factory(): + created_users = [] + + def create_user(**kwargs): + user = User(**kwargs) + created_users.append(user) + return user + + yield create_user + + # Cleanup + for user in created_users: + user.delete() + +def test_multiple_users(user_factory): + user1 = user_factory(name="Alice") + user2 = user_factory(name="Bob") + assert user1.name != user2.name +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/index.md b/.tessl/tiles/tessl/pypi-pytest/docs/index.md new file mode 100644 index 000000000..376739e8f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/index.md @@ -0,0 +1,466 @@ +# pytest + +A comprehensive Python testing framework that makes it easy to write simple tests while scaling to support complex functional testing for applications and libraries. pytest features detailed assertion introspection using plain assert statements, automatic test discovery, modular fixtures for managing test resources, and a rich plugin architecture with over 1300 external plugins. + +## Package Information + +- **Package Name**: pytest +- **Language**: Python +- **Installation**: `pip install pytest` +- **Version**: 8.4.2 +- **License**: MIT +- **Python Compatibility**: Python 3.9+ (3.9, 3.10, 3.11, 3.12, 3.13, 3.14) + +## Core Imports + +```python +import pytest +``` + +For accessing specific functionality: + +```python +from pytest import ( + fixture, raises, skip, xfail, fail, + mark, param, approx, Config, Session +) +``` + +## Basic Usage + +```python +import pytest + +# Simple test function +def test_basic_math(): + assert 2 + 2 == 4 + assert 5 * 3 == 15 + +# Test with fixture +@pytest.fixture +def sample_data(): + return [1, 2, 3, 4, 5] + +def test_with_fixture(sample_data): + assert len(sample_data) == 5 + assert sum(sample_data) == 15 + +# Parametrized test +@pytest.mark.parametrize("input,expected", [ + (2, 4), + (3, 9), + (4, 16) +]) +def test_square(input, expected): + assert input ** 2 == expected + +# Exception testing +def test_exception(): + with pytest.raises(ValueError): + int("invalid") + +# Skipping tests +@pytest.mark.skip(reason="Not implemented yet") +def test_future_feature(): + pass + +# Expected failure +@pytest.mark.xfail(reason="Known bug") +def test_known_issue(): + assert False +``` + +## Architecture + +pytest's architecture is built around several key components: + +- **Collection System**: Automatic discovery and collection of test files, classes, and functions using a tree-based collector hierarchy +- **Fixture System**: Dependency injection framework providing reusable test components with automatic cleanup and parametrization +- **Plugin Architecture**: Hook-based system enabling extensibility through 1300+ community plugins and custom implementations +- **Configuration Management**: Comprehensive configuration through command-line options, ini files, and programmatic access +- **Assertion Introspection**: Advanced assertion rewriting providing detailed failure information without custom assertion methods +- **Mark System**: Flexible test categorization and parametrization supporting conditional execution and metadata attachment + +This design enables pytest to scale from simple unit tests to complex functional testing scenarios while maintaining ease of use and extensive customization capabilities. + +## Capabilities + +### Core Testing Functions + +Essential functions for controlling test execution flow including skip, xfail, fail, exit, and conditional imports. These functions provide the fundamental building blocks for test control and outcome management. + +```python { .api } +def skip(reason: str = "", *, allow_module_level: bool = False): ... +def xfail(reason: str = ""): ... +def fail(reason: str = "", pytrace: bool = True): ... +def exit(reason: str = "", returncode: int | None = None): ... +def importorskip(modname: str, minversion: str | None = None, reason: str | None = None, *, exc_type: type[ImportError] | None = None): ... +``` + +[Testing Functions](./testing-functions.md) + +### Exception Handling and Assertions + +Context managers and utilities for asserting exceptions, warnings, and approximate numerical comparisons. Includes comprehensive exception information and group exception handling. + +```python { .api } +def raises(expected_exception, *, match=None, check=None) -> RaisesExc: ... +def warns(expected_warning, *, match=None) -> WarningsRecorder: ... +def deprecated_call(func=None, *args, **kwargs): ... +def approx(expected, rel=None, abs=None, nan_ok: bool = False): ... +``` + +[Assertions](./assertions.md) + +### Fixture System and Dependency Injection + +Comprehensive fixture system for managing test dependencies, setup, and teardown with support for multiple scopes, parametrization, and automatic resolution. + +```python { .api } +def fixture(fixture_function=None, *, scope="function", params=None, autouse=False, ids=None, name=None): ... + +class FixtureRequest: + def getfixturevalue(self, argname: str): ... + def applymarker(self, marker): ... +``` + +[Fixtures](./fixtures.md) + +### Configuration and Plugin System + +Central configuration management and plugin architecture enabling extensive customization through hooks, configuration files, and command-line options. + +```python { .api } +class Config: + def getini(self, name: str): ... + def getoption(self, name: str): ... + def addinivalue_line(self, name: str, line: str): ... + +class PytestPluginManager: + def register(self, plugin, name=None): ... + def unregister(self, plugin=None, name=None): ... + +def hookimpl(**kwargs): ... +def hookspec(**kwargs): ... +``` + +[Configuration](./configuration.md) + +### Test Collection and Execution + +Collection tree components for test discovery, organization, and execution including nodes, collectors, items, and session management. + +```python { .api } +class Session: + def collect(self): ... + def pytest_runtest_protocol(self, item): ... + +class Function: + def runtest(self): ... + def setup(self): ... + def teardown(self): ... + +class Metafunc: + def parametrize(self, argnames, argvalues, **kwargs): ... +``` + +[Test Collection](./test-collection.md) + +### Test Utilities and Environment Control + +Utilities for controlling test environments including monkeypatching, output capture, temporary paths, caching, and testing framework testing tools. + +```python { .api } +class MonkeyPatch: + def setattr(self, target, name, value=, raising: bool = True): ... + def setenv(self, name: str, value: str, prepend: str | None = None): ... + def chdir(self, path): ... + +class CaptureFixture: + def readouterr(self): ... + def disabled(self): ... + +class Cache: + def get(self, key: str, default): ... + def set(self, key: str, value: object) -> None: ... + def mkdir(self, name: str) -> Path: ... + +class Stash: + def __setitem__(self, key: StashKey[T], value: T) -> None: ... + def __getitem__(self, key: StashKey[T]) -> T: ... + def get(self, key: StashKey[T], default: D) -> T | D: ... + +class StashKey(Generic[T]): + """Type-safe key for Stash storage.""" + +class Pytester: + def makepyfile(self, **kwargs): ... + def runpytest(self, *args, **kwargs): ... +``` + +[Test Utilities](./test-utilities.md) + +### Marks and Parametrization + +Mark system for test categorization, parametrization, and metadata attachment enabling flexible test selection and data-driven testing. + +```python { .api } +class MarkGenerator: + def parametrize(self, argnames, argvalues, **kwargs): ... + def skip(self, reason: str = ""): ... + def skipif(self, condition, *, reason: str = ""): ... + def xfail(self, condition=True, *, reason: str = "", raises=None, run: bool = True, strict: bool = False): ... + +def param(*values, marks=(), id=None): ... + +# Access via pytest.mark +mark: MarkGenerator +``` + +[Marks and Parametrization](./marks.md) + +### Test Reporting and Results + +Comprehensive reporting system for test execution results, collection reports, and terminal output formatting with detailed information about test outcomes. + +```python { .api } +class TestReport: + def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: ... + +class CollectReport: + def _to_json(self) -> dict[str, Any]: ... + +class CallInfo(Generic[TResult]): + def from_call(cls, func: Callable[[], TResult], when: str, reraise=None) -> CallInfo[TResult]: ... + +class TerminalReporter: + def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: ... + def pytest_runtest_logreport(self, report: TestReport) -> None: ... +``` + +[Test Reporting](./reporting.md) + +### Warning Types and Error Handling + +Comprehensive warning type hierarchy for pytest-specific warnings including deprecation warnings, configuration warnings, and collection warnings. + +```python { .api } +class PytestWarning(UserWarning): ... +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): ... +class PytestConfigWarning(PytestWarning): ... +class PytestCollectionWarning(PytestWarning): ... +class PytestUnknownMarkWarning(PytestWarning): ... +``` + +[Warning Types](./warnings.md) + +## Entry Points + +```python { .api } +def main( + args: list[str] | os.PathLike[str] | None = None, + plugins: Sequence[str | _PluggyPlugin] | None = None, +) -> int | ExitCode: + """ + Main entry point for running pytest programmatically. + + Performs an in-process test run with comprehensive configuration + and plugin management. + + Parameters: + - args: Command line arguments. If None, uses sys.argv. Can be list + of strings or single PathLike object + - plugins: Plugin objects or names to auto-register during initialization + + Returns: + Exit code (0 for success, non-zero for failure/errors) + + Examples: + >>> pytest.main() # Run with default args + >>> pytest.main(['-v', 'tests/']) # With specific arguments + >>> pytest.main(['tests/'], plugins=['my_plugin']) # With plugins + """ + +def console_main() -> int: + """ + Console script entry point for pytest command. + + This is the function called when running 'pytest' from command line. + Not intended for programmatic use - use main() instead. + + Returns: + Integer exit code for console applications + + Note: + Handles BrokenPipeError gracefully and flushes stdout before returning. + """ + +class cmdline: + """Compatibility namespace for legacy pytest usage.""" + + main = staticmethod(main) # Provides pytest.cmdline.main() compatibility +``` + +**Usage Examples:** + +```python +import pytest +from pathlib import Path + +# Basic programmatic usage +def run_tests(): + exit_code = pytest.main(['-v', '--tb=short']) + if exit_code == 0: + print("All tests passed!") + return exit_code + +# With custom configuration +def run_specific_tests(): + args = [ + 'tests/unit/', # Test directory + '-v', # Verbose output + '--maxfail=3', # Stop after 3 failures + '--tb=short', # Short traceback format + '-x' # Stop on first failure + ] + return pytest.main(args) + +# With plugins +def run_with_plugins(): + return pytest.main( + ['tests/'], + plugins=['pytest-html', 'pytest-cov'] + ) + +# Legacy compatibility usage +def legacy_style(): + return pytest.cmdline.main(['-v', 'tests/']) + +# Handling results +def test_runner_with_handling(): + result = pytest.main(['tests/integration/']) + + if result == pytest.ExitCode.OK: + print("Tests passed successfully") + elif result == pytest.ExitCode.TESTS_FAILED: + print("Some tests failed") + elif result == pytest.ExitCode.NO_TESTS_RAN: + print("No tests were collected") + elif result == pytest.ExitCode.INTERRUPTED: + print("Test run was interrupted") + else: + print(f"Test run failed with exit code: {result}") + + return result +``` + +## Types + +```python { .api } +from enum import Enum + +class ExitCode(Enum): + OK = 0 + TESTS_FAILED = 1 + INTERRUPTED = 2 + INTERNAL_ERROR = 3 + USAGE_ERROR = 4 + NO_TESTS_RAN = 5 + +class UsageError(Exception): + """Raised for command-line usage errors.""" + +class FixtureLookupError(Exception): + """Raised when fixture cannot be found or resolved.""" +``` + +## Built-in Fixtures + +pytest provides many built-in fixtures automatically available in all tests: + +```python { .api } +# Core configuration and request +def test_core_fixtures( + request, # FixtureRequest: Access to test context and other fixtures + config, # Config: pytest configuration object + pytestconfig, # Config: Alias for config fixture +): + pass + +# Output capture +def test_capture_fixtures( + capsys, # CaptureFixture: Capture sys.stdout/stderr (text) + capsysbinary, # CaptureFixture: Capture sys.stdout/stderr (binary) + capfd, # CaptureFixture: Capture file descriptors 1/2 (text) + capfdbinary, # CaptureFixture: Capture file descriptors 1/2 (binary) + caplog, # LogCaptureFixture: Capture log messages +): + pass + +# Environment modification +def test_environment_fixtures( + monkeypatch, # MonkeyPatch: Modify objects, environment, sys.path +): + pass + +# Temporary paths +def test_temp_fixtures( + tmp_path, # pathlib.Path: Temporary directory (function scope) + tmp_path_factory, # TempPathFactory: Create additional temp directories + tmpdir, # py.path.local: Legacy temp directory (deprecated) + tmpdir_factory, # TempdirFactory: Legacy temp factory (deprecated) +): + pass + +# Warning handling +def test_warning_fixtures( + recwarn, # WarningsRecorder: Record warnings during test +): + pass + +# Plugin testing (requires pytest plugin) +def test_plugin_fixtures( + pytester, # Pytester: Test pytest plugins and functionality + testdir, # Testdir: Legacy plugin testing (deprecated) +): + pass + +# Doctest integration (requires doctest plugin) +def test_doctest_fixtures( + doctest_namespace, # dict: Namespace for doctest execution +): + pass +``` + +## Version Information + +```python { .api } +__version__: str # Current pytest version string (e.g., "8.4.2") +version_tuple: tuple[int, ...] # Version as tuple of integers (e.g., (8, 4, 2)) +``` + +**Usage Examples:** + +```python +import pytest + +# Check pytest version +print(f"pytest version: {pytest.__version__}") +# Output: pytest version: 8.4.2 + +# Version tuple for programmatic comparison +version = pytest.version_tuple +if version >= (8, 0, 0): + print("Using modern pytest features") + +# Version checking utility +def require_pytest_version(min_version_tuple): + if pytest.version_tuple < min_version_tuple: + raise RuntimeError( + f"pytest {'.'.join(map(str, min_version_tuple))} or higher required, " + f"found {pytest.__version__}" + ) + +# Example usage +require_pytest_version((7, 0, 0)) # Ensure pytest 7.0.0+ +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/marks.md b/.tessl/tiles/tessl/pypi-pytest/docs/marks.md new file mode 100644 index 000000000..b9629daf5 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/marks.md @@ -0,0 +1,364 @@ +# Marks and Parametrization + +Mark system for test categorization, parametrization, and metadata attachment enabling flexible test selection and data-driven testing. The mark system provides a powerful way to organize and control test execution. + +## Capabilities + +### Mark Generation + +Dynamic mark creation system providing access to built-in and custom marks. + +```python { .api } +class MarkGenerator: + """Generates mark decorators dynamically.""" + + def __getattr__(self, name: str) -> MarkDecorator: + """Create MarkDecorator for any mark name.""" + + # Built-in marks + def parametrize( + self, + argnames: str | list[str] | tuple[str, ...], + argvalues, + *, + indirect: bool | list[str] = False, + ids=None, + scope: str | None = None + ) -> MarkDecorator: + """Parametrize test function.""" + + def skip(self, reason: str = "") -> MarkDecorator: + """Skip test unconditionally.""" + + def skipif( + self, + condition, + *, + reason: str = "" + ) -> MarkDecorator: + """Skip test conditionally.""" + + def xfail( + self, + condition=True, + *, + reason: str = "", + raises=None, + run: bool = True, + strict: bool = False + ) -> MarkDecorator: + """Mark test as expected to fail.""" + + def usefixtures(self, *names: str) -> MarkDecorator: + """Use fixtures without declaring them as arguments.""" + + def filterwarnings(self, *filters: str) -> MarkDecorator: + """Filter warnings for specific test.""" + +# Global mark instance +mark: MarkGenerator +``` + +**Usage Example:** + +```python +import pytest + +# Basic marks +@pytest.mark.slow +def test_slow_operation(): + pass + +@pytest.mark.skip(reason="Not implemented yet") +def test_future_feature(): + pass + +@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires Python 3.9+") +def test_new_feature(): + pass + +@pytest.mark.xfail(reason="Known bug") +def test_known_issue(): + assert False + +# Custom marks +@pytest.mark.integration +@pytest.mark.database +def test_database_integration(): + pass + +# Using fixtures without parameters +@pytest.mark.usefixtures("setup_test_data", "cleanup_temp_files") +def test_with_automatic_fixtures(): + pass + +# Filter warnings +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_legacy_code(): + pass +``` + +### Mark Decorators + +Decorators that apply marks to test functions and classes. + +```python { .api } +class MarkDecorator: + """Decorator that applies marks to test functions and classes.""" + + # Attributes + mark: Mark # The underlying mark + markname: str # Mark name + args: tuple # Mark arguments + kwargs: dict # Mark keyword arguments + + def __call__(self, func_or_class): + """Apply mark to function or class.""" + + def with_args(self, *args, **kwargs) -> MarkDecorator: + """Create new decorator with additional arguments.""" + + def combine(self, other: MarkDecorator) -> MarkDecorator: + """Combine with another mark decorator.""" +``` + +### Mark Objects + +Represents a mark applied to a test with its arguments and metadata. + +```python { .api } +class Mark: + """Represents a mark applied to a test.""" + + # Attributes + name: str # Mark name + args: tuple # Mark arguments + kwargs: dict # Mark keyword arguments + + def combined_with(self, other: Mark) -> Mark: + """Combine with another mark.""" + + def _for_parametrize(self) -> Mark: + """Create mark suitable for parametrization.""" +``` + +### Test Parametrization + +Create parameter sets for data-driven testing. + +```python { .api } +def param(*values, marks=(), id=None): + """ + Create parameter sets for pytest.mark.parametrize. + + Parameters: + - values: Parameter values for the test + - marks: Marks to apply to this parameter set + - id: Identifier for this parameter set + + Returns: + ParameterSet object + """ + +class ParameterSet: + """Represents a set of parameters for parametrized tests.""" + + # Attributes + values: tuple # Parameter values + marks: tuple[Mark, ...] # Applied marks + id: str | None # Parameter set identifier + + @classmethod + def param(cls, *values, marks=(), id=None) -> ParameterSet: + """Create parameter set.""" + + @classmethod + def extract_from( + cls, + parameterset, + legacy_force_tuple: bool = False + ) -> ParameterSet: + """Extract parameter set from various input formats.""" +``` + +**Usage Example:** + +```python +import pytest + +# Basic parametrization +@pytest.mark.parametrize("input,expected", [ + (2, 4), + (3, 9), + (4, 16), +]) +def test_square(input, expected): + assert input ** 2 == expected + +# Named parameters with IDs +@pytest.mark.parametrize( + "operation,a,b,expected", + [ + ("add", 2, 3, 5), + ("multiply", 4, 5, 20), + ("subtract", 10, 3, 7), + ], + ids=["addition", "multiplication", "subtraction"] +) +def test_math_operations(operation, a, b, expected): + if operation == "add": + assert a + b == expected + elif operation == "multiply": + assert a * b == expected + elif operation == "subtract": + assert a - b == expected + +# Using pytest.param with marks +@pytest.mark.parametrize("value", [ + 1, + 2, + pytest.param(3, marks=pytest.mark.xfail(reason="Known issue with 3")), + pytest.param(4, marks=pytest.mark.skip(reason="Skip 4 for now")), + pytest.param(5, id="special_five"), +]) +def test_values(value): + assert value < 10 + +# Multiple parametrizations +@pytest.mark.parametrize("browser", ["chrome", "firefox"]) +@pytest.mark.parametrize("os", ["windows", "linux", "mac"]) +def test_cross_platform(browser, os): + # Runs 6 times (2 browsers × 3 operating systems) + pass + +# Indirect parametrization (parameters passed to fixtures) +@pytest.fixture +def database(request): + db_type = request.param + return setup_database(db_type) + +@pytest.mark.parametrize("database", ["sqlite", "postgresql"], indirect=True) +def test_with_database(database): + # database fixture receives "sqlite" or "postgresql" as request.param + pass +``` + +### Mark Constants + +Special constants for controlling mark behavior. + +```python { .api } +HIDDEN_PARAM = object() # Special value to hide parameters from test names +``` + +**Usage Example:** + +```python +@pytest.mark.parametrize("value,description", [ + (1, "first"), + (2, "second"), + (pytest.HIDDEN_PARAM, "hidden"), # This parameter won't appear in test name +]) +def test_with_hidden_param(value, description): + pass +``` + +## Built-in Marks + +### Skip Marks + +```python +# Unconditional skip +@pytest.mark.skip(reason="Not implemented") + +# Conditional skip +@pytest.mark.skipif(condition, reason="Explanation") +@pytest.mark.skipif(sys.platform == "win32", reason="Unix only") +@pytest.mark.skipif(not has_feature(), reason="Feature not available") +``` + +### Expected Failure Marks + +```python +# Basic expected failure +@pytest.mark.xfail(reason="Known bug") + +# Conditional expected failure +@pytest.mark.xfail(sys.version_info < (3, 9), reason="Requires Python 3.9+") + +# Strict xfail (failure if test unexpectedly passes) +@pytest.mark.xfail(strict=True, reason="Should definitely fail") + +# Expected specific exception +@pytest.mark.xfail(raises=ValueError, reason="Should raise ValueError") + +# Don't run, just mark as xfail +@pytest.mark.xfail(run=False, reason="Don't run this test") +``` + +### Parametrization Marks + +```python +# Simple parametrization +@pytest.mark.parametrize("value", [1, 2, 3]) + +# Multiple parameters +@pytest.mark.parametrize("a,b,expected", [(1, 2, 3), (2, 3, 5)]) + +# Custom IDs +@pytest.mark.parametrize("value", [1, 2, 3], ids=["one", "two", "three"]) + +# Indirect parametrization +@pytest.mark.parametrize("fixture_name", ["value1", "value2"], indirect=True) +``` + +### Fixture Marks + +```python +# Use fixtures without declaring as parameters +@pytest.mark.usefixtures("setup_database", "cleanup_temp_files") + +# Filter warnings +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +@pytest.mark.filterwarnings("error::UserWarning") +``` + +## Running Tests with Marks + +Command-line options for running specific marked tests: + +```bash +# Run only tests with specific mark +pytest -m "slow" + +# Run tests with multiple marks +pytest -m "slow and integration" + +# Run tests without specific mark +pytest -m "not slow" + +# Complex mark expressions +pytest -m "(slow or integration) and not windows" +``` + +## Custom Marks + +Register custom marks in pytest configuration: + +```python +# pytest.ini +[tool:pytest] +markers = + slow: marks tests as slow (deselect with '-m "not slow"') + integration: marks tests as integration tests + unit: marks tests as unit tests + smoke: marks tests as smoke tests + regression: marks tests as regression tests + +# pyproject.toml +[tool.pytest.ini_options] +markers = [ + "slow: marks tests as slow", + "integration: marks tests as integration tests", +] +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/reporting.md b/.tessl/tiles/tessl/pypi-pytest/docs/reporting.md new file mode 100644 index 000000000..4b3c814e3 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/reporting.md @@ -0,0 +1,538 @@ +# Test Reporting and Results + +Comprehensive reporting system for test execution results, collection reports, and terminal output formatting. These classes provide detailed information about test outcomes and handle presentation of results to users. + +## Capabilities + +### Test Execution Reports + +Classes representing the results of test execution. + +```python { .api } +@dataclasses.dataclass +class CallInfo(Generic[TResult]): + """Result/Exception info of a function invocation.""" + + def __init__( + self, + result: TResult | None, + excinfo: ExceptionInfo[BaseException] | None, + start: float, + stop: float, + duration: float, + when: Literal["collect", "setup", "call", "teardown"], + _ispytest: bool = False, + ) -> None: + """ + Initialize call information. + + Parameters: + - result: Return value (None if exception occurred) + - excinfo: Exception info if raised + - start: Start time (seconds since epoch) + - stop: End time (seconds since epoch) + - duration: Call duration in seconds + - when: Invocation context + - _ispytest: Internal pytest flag + """ + + # Attributes + _result: TResult | None # Private result storage + excinfo: ExceptionInfo[BaseException] | None # Captured exception + start: float # Start timestamp + stop: float # End timestamp + duration: float # Execution duration + when: str # Context of invocation ("collect", "setup", "call", "teardown") + + @property + def result(self) -> TResult: + """ + Get the call result. + + Returns: + The function result + + Raises: + AttributeError: If excinfo is not None (exception occurred) + """ + + @classmethod + def from_call( + cls, + func: Callable[[], TResult], + when: str, + reraise=None + ) -> CallInfo[TResult]: + """ + Execute function and capture call information. + + Parameters: + - func: Function to execute + - when: Context identifier + - reraise: Exception types to reraise immediately + + Returns: + CallInfo with execution results and timing + """ + +class TestReport(BaseReport): + """Basic test report object for test execution results. + + Also used for setup and teardown calls if they fail. + Reports can contain arbitrary extra attributes. + """ + + def __init__( + self, + nodeid: str, + location: tuple[str, int | None, str], + keywords: Mapping[str, Any], + outcome: Literal["passed", "failed", "skipped"], + longrepr: None | ExceptionInfo | tuple[str, int, str] | str | TerminalRepr, + when: Literal["setup", "call", "teardown"], + sections: Iterable[tuple[str, str]] = (), + duration: float = 0, + start: float = 0, + stop: float = 0, + user_properties: Iterable[tuple[str, object]] | None = None, + **extra + ) -> None: + """ + Initialize test report. + + Parameters: + - nodeid: Normalized collection nodeid + - location: File path, line number, domain info + - keywords: Name->value dict of keywords/markers + - outcome: Test outcome ("passed", "failed", "skipped") + - longrepr: Failure representation + - when: Test phase ("setup", "call", "teardown") + - sections: Extra information tuples (heading, content) + - duration: Test execution time in seconds + - start: Start time (seconds since epoch) + - stop: End time (seconds since epoch) + - user_properties: User-defined properties + - **extra: Additional arbitrary attributes + """ + + # Core attributes + nodeid: str # Test identifier + location: tuple[str, int | None, str] # Test location info + keywords: Mapping[str, Any] # Associated keywords and markers + outcome: str # One of "passed", "failed", "skipped" + longrepr # Failure representation (None for passed tests) + when: str # Phase: "setup", "call", or "teardown" + user_properties: list[tuple[str, object]] # User-defined test properties + sections: list[tuple[str, str]] # Captured output sections + duration: float # Execution duration + start: float # Start timestamp + stop: float # End timestamp + wasxfail: str # XFail reason (if applicable) + + @classmethod + def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: + """ + Create TestReport from test item and call info. + + Parameters: + - item: Test item that was executed + - call: Call information from execution + + Returns: + TestReport with complete execution information + """ + + def _to_json(self) -> dict[str, Any]: + """ + Serialize report to JSON-compatible dict. + + Returns: + Dictionary representation for JSON serialization + """ + + @classmethod + def _from_json(cls, reportdict: dict[str, object]) -> TestReport: + """ + Deserialize TestReport from JSON dict. + + Parameters: + - reportdict: Dictionary from JSON deserialization + + Returns: + TestReport instance + """ + +class CollectReport(BaseReport): + """Collection report object representing collection results. + + Reports can contain arbitrary extra attributes. + """ + + def __init__( + self, + nodeid: str, + outcome: Literal["passed", "failed", "skipped"], + longrepr: None | ExceptionInfo | tuple[str, int, str] | str | TerminalRepr, + result: list[Item | Collector] | None, + sections: Iterable[tuple[str, str]] = (), + **extra + ) -> None: + """ + Initialize collection report. + + Parameters: + - nodeid: Normalized collection nodeid + - outcome: Collection outcome ("passed", "failed", "skipped") + - longrepr: Failure representation + - result: Collected items and nodes + - sections: Extra information sections + - **extra: Additional arbitrary attributes + """ + + # Attributes + when = "collect" # Always "collect" for collection reports + nodeid: str # Collection node identifier + outcome: str # Collection result + longrepr # Collection failure details (if any) + result: list[Item | Collector] # Successfully collected items + sections: list[tuple[str, str]] # Captured output during collection + + @property + def location(self) -> tuple[str, None, str]: + """ + Get collection location. + + Returns: + Tuple of (fspath, None, fspath) + """ + + def _to_json(self) -> dict[str, Any]: + """ + Serialize report to JSON-compatible dict. + + Returns: + Dictionary representation for JSON serialization + """ + + @classmethod + def _from_json(cls, reportdict: dict[str, object]) -> CollectReport: + """ + Deserialize CollectReport from JSON dict. + + Parameters: + - reportdict: Dictionary from JSON deserialization + + Returns: + CollectReport instance + """ +``` + +### Terminal Output and Formatting + +Classes controlling terminal output during test execution. + +```python { .api } +class TestShortLogReport(NamedTuple): + """Container for test status information used in terminal reporting. + + Used to store the test status result category, shortletter and verbose word. + For example "rerun", "R", ("RERUN", {"yellow": True}). + """ + + category: str # Result class ("passed", "skipped", "error", or empty) + letter: str # Short letter for progress (".", "s", "E", or empty) + word: str | tuple[str, Mapping[str, bool]] # Verbose word or (word, markup) tuple + +class TerminalReporter: + """Controls terminal output formatting and reporting during test execution. + + Main class responsible for all terminal output including progress indicators, + test results, summaries, and formatting. + """ + + def __init__(self, config: Config, file: TextIO | None = None) -> None: + """ + Initialize terminal reporter. + + Parameters: + - config: pytest configuration object + - file: Output stream (defaults to sys.stdout) + """ + + # Core attributes + config: Config # pytest configuration + stats: dict[str, list[Any]] # Statistics by category (failed, passed, etc.) + _tw: TerminalWriter # Terminal writer for output + reportchars: str # Characters controlling what to report + hasmarkup: bool # Whether terminal supports markup + isatty: bool # Whether output is to a terminal + verbosity: int # Verbosity level + showfspath: bool # Whether to show file paths + showlongtestinfo: bool # Whether to show detailed test info + _session: Session | None # Current test session + _numcollected: int # Number of collected items + currentfspath: Path | str | int | None # Current file being processed + + def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: + """ + Write content to terminal. + + Parameters: + - content: Content to write + - flush: Whether to flush output immediately + - **markup: Markup options (bold, red, etc.) + """ + + def write_line(self, line: str | bytes, **markup: bool) -> None: + """ + Write line with newline to terminal. + + Parameters: + - line: Line content + - **markup: Markup options + """ + + def write_sep(self, sep: str, title: str | None = None, **markup: bool) -> None: + """ + Write separator line. + + Parameters: + - sep: Separator character + - title: Optional title to include + - **markup: Markup options + """ + + def rewrite(self, line: str, **markup: bool) -> None: + """ + Rewrite current line (for progress indicators). + + Parameters: + - line: New line content + - **markup: Markup options + """ + + def section(self, title: str, sep: str = "=", **kw: bool) -> None: + """ + Write section header. + + Parameters: + - title: Section title + - sep: Separator character + - **kw: Keyword arguments for formatting + """ + + def pytest_runtest_logreport(self, report: TestReport) -> None: + """ + Handle test report logging. + + Parameters: + - report: Test execution report + """ + + def pytest_collectreport(self, report: CollectReport) -> None: + """ + Handle collection report logging. + + Parameters: + - report: Collection report + """ + + def pytest_sessionstart(self, session: Session) -> None: + """ + Handle session start. + + Parameters: + - session: Test session + """ + + def pytest_sessionfinish(self, session: Session, exitstatus: int | ExitCode) -> None: + """ + Handle session finish. + + Parameters: + - session: Test session + - exitstatus: Exit status code + """ + + def summary_failures(self) -> None: + """Show failure summary section.""" + + def summary_errors(self) -> None: + """Show error summary section.""" + + def summary_warnings(self) -> None: + """Show warning summary section.""" + + def short_test_summary(self) -> None: + """Show short test summary section.""" + + def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]: + """ + Build final statistics line. + + Returns: + Tuple of (stat_parts, duration_string) + """ +``` + +## Usage Examples + +### Creating Custom Reports + +```python +import pytest +from _pytest.reports import TestReport, CollectReport + +# Custom plugin to process test reports +class CustomReportPlugin: + def __init__(self): + self.test_results = [] + + def pytest_runtest_logreport(self, report: TestReport): + # Process test reports + if report.when == "call": # Only process main test execution + self.test_results.append({ + 'nodeid': report.nodeid, + 'outcome': report.outcome, + 'duration': report.duration, + 'location': report.location, + 'user_properties': report.user_properties, + }) + + def pytest_collection_finish(self, session): + # Process collection results + for item in session.items: + print(f"Collected test: {item.nodeid}") + +# Register plugin +pytest.main(["-p", "no:terminal", "--tb=no"], plugins=[CustomReportPlugin()]) +``` + +### Custom Terminal Output + +```python +from _pytest.terminal import TerminalReporter + +class ColorfulReporter(TerminalReporter): + def pytest_runtest_logreport(self, report): + # Custom colored output + if report.when == "call": + if report.outcome == "passed": + self.write("✓ ", green=True) + elif report.outcome == "failed": + self.write("✗ ", red=True) + elif report.outcome == "skipped": + self.write("- ", yellow=True) + + self.write_line(f"{report.nodeid}") + + # Call parent implementation for other functionality + super().pytest_runtest_logreport(report) + +# Use custom reporter +def pytest_configure(config): + if config.pluginmanager.get_plugin("terminalreporter"): + config.pluginmanager.unregister(name="terminalreporter") + + config.pluginmanager.register(ColorfulReporter(config), "terminalreporter") +``` + +### Accessing Call Information + +```python +from _pytest.runner import CallInfo + +def test_with_call_info(): + # CallInfo is typically used internally, but can be accessed in hooks + def example_function(): + return "result" + + # Capture call information + call_info = CallInfo.from_call(example_function, when="call") + + assert call_info.result == "result" + assert call_info.excinfo is None + assert call_info.duration > 0 + assert call_info.when == "call" + +# Plugin to access call information in hooks +def pytest_runtest_call(pyfuncitem): + # This hook has access to CallInfo through the test execution + pass + +def pytest_runtest_makereport(item, call): + # call parameter is a CallInfo object + if call.when == "call" and call.excinfo is None: + print(f"Test {item.nodeid} passed in {call.duration:.2f}s") +``` + +## Integration with Reporting Tools + +### JSON Report Generation + +```python +import json +from _pytest.reports import TestReport, CollectReport + +class JSONReporter: + def __init__(self): + self.reports = [] + + def pytest_runtest_logreport(self, report: TestReport): + # Convert TestReport to JSON-serializable format + report_data = report._to_json() + self.reports.append(report_data) + + def pytest_sessionfinish(self, session): + # Write JSON report + with open("test_report.json", "w") as f: + json.dump(self.reports, f, indent=2) + +# Usage +pytest.main(["--tb=short"], plugins=[JSONReporter()]) +``` + +### Database Integration + +```python +import sqlite3 +from _pytest.reports import TestReport + +class DatabaseReporter: + def __init__(self): + self.conn = sqlite3.connect("test_results.db") + self.setup_database() + + def setup_database(self): + self.conn.execute(""" + CREATE TABLE IF NOT EXISTS test_results ( + nodeid TEXT, + outcome TEXT, + duration REAL, + when_phase TEXT, + timestamp REAL, + location_file TEXT, + location_line INTEGER + ) + """) + + def pytest_runtest_logreport(self, report: TestReport): + if report.when == "call": + self.conn.execute(""" + INSERT INTO test_results + (nodeid, outcome, duration, when_phase, timestamp, location_file, location_line) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, ( + report.nodeid, + report.outcome, + report.duration, + report.when, + report.start, + report.location[0], + report.location[1] + )) + self.conn.commit() + +# Usage with database integration +pytest.main(["-v"], plugins=[DatabaseReporter()]) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/test-collection.md b/.tessl/tiles/tessl/pypi-pytest/docs/test-collection.md new file mode 100644 index 000000000..5fe3fdc7a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/test-collection.md @@ -0,0 +1,528 @@ +# Test Collection and Execution + +Collection tree components for test discovery, organization, and execution. The collection system builds a hierarchical tree of collectors and items that represent the structure of your test suite. + +## Capabilities + +### Base Collection Classes + +Foundation classes for all collection tree components. + +```python { .api } +class Node: + """Base class for all collection tree components.""" + + # Core attributes + name: str # Node name + parent: Node | None # Parent node + config: Config # pytest configuration + session: Session # Test session + path: Path # File system path + nodeid: str # Unique node identifier + + @classmethod + def from_parent(cls, parent: Node, **kwargs): + """Create node with parent reference.""" + + def listchain(self) -> list[Node]: + """Return list of all parent nodes.""" + + def add_marker(self, marker) -> None: + """Add marker to this node.""" + + def iter_markers(self, name: str | None = None): + """Iterate over markers.""" + + def get_closest_marker(self, name: str): + """Get closest marker with given name.""" + +class Collector(Node, abc.ABC): + """Base class for collection tree internal nodes. + + Collectors create children through collect() and iteratively build + the collection tree. + """ + + class CollectError(Exception): + """An error during collection, contains a custom message.""" + + @abc.abstractmethod + def collect(self) -> Iterable[Item | Collector]: + """ + Collect children (items and collectors) for this collector. + + Returns: + Iterable of Item or Collector objects + """ + + def repr_failure(self, excinfo: ExceptionInfo[BaseException]) -> str | TerminalRepr: + """ + Return a representation of a collection failure. + + Parameters: + - excinfo: Exception information for the failure + + Returns: + String or TerminalRepr representation of the failure + """ + +class Item(Node, abc.ABC): + """Base class for collection tree leaf nodes (test items). + + Note that for a single function there might be multiple test invocation items. + """ + + nextitem = None # Reference to next item + + def __init__( + self, + name, + parent=None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + **kw, + ) -> None: + """ + Initialize test item. + + Parameters: + - name: Item name + - parent: Parent collector + - config: pytest configuration + - session: Test session + - nodeid: Node identifier + """ + + @abc.abstractmethod + def runtest(self) -> None: + """ + Run the test case (abstract method). + + Must be implemented by subclasses to execute the actual test. + """ + + def setup(self) -> None: + """Set up test execution.""" + + def teardown(self) -> None: + """Tear down after test execution.""" + + def add_report_section(self, when: str, key: str, content: str) -> None: + """ + Add report section to test results. + + Parameters: + - when: Test phase ("setup", "call", "teardown") + - key: Section identifier + - content: Section content + """ + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + """ + Return location information for reporting. + + Returns: + Tuple of (filename, line_number, test_name) + """ + + @property + def location(self) -> tuple[str, int | None, str]: + """Get test location as (relfspath, lineno, testname).""" + + # Attributes + user_properties: list[tuple[str, object]] # User-defined properties + _report_sections: list[tuple[str, str, str]] # Report sections +``` + +### File System Collectors + +Collectors for file system entities. + +```python { .api } +class FSCollector(Collector, abc.ABC): + """Base class for filesystem-based collectors.""" + + def __init__( + self, + path: Path, + parent: Collector | None = None, + config: Config | None = None, + session: Session | None = None, + nodeid: str | None = None, + ) -> None: + """Initialize filesystem collector with path.""" + +class File(FSCollector, abc.ABC): + """Base class for collecting tests from files.""" + + @abc.abstractmethod + def collect(self) -> Iterable[Item | Collector]: + """Collect tests from file (abstract method).""" + +class Directory(FSCollector, abc.ABC): + """Base class for collecting from directories.""" + + @abc.abstractmethod + def collect(self) -> Iterable[Item | Collector]: + """Collect from directory contents (abstract method).""" + +class Dir(Directory): + """Directory collector implementation. + + Used for directories without __init__.py files. + """ + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + """ + Collect files and subdirectories. + + Returns: + Iterable of collectors for Python files and subdirectories + """ + + @classmethod + def from_parent(cls, parent: nodes.Collector, *, path: Path) -> Self: + """ + Create Dir collector from parent. + + Parameters: + - parent: Parent collector + - path: Directory path + + Returns: + Dir collector instance + """ +``` + +### Python-Specific Collectors + +Collectors specialized for Python test code. + +```python { .api } +class PyCollector(Collector, abc.ABC): + """Base class for Python-specific collectors.""" + + def _getobj(self): + """Import and return the Python object.""" + +class Module(nodes.File, PyCollector): + """Collector for test classes and functions in Python modules.""" + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + """ + Collect test classes and functions from module. + + Returns: + Iterable of Class collectors and Function items + """ + + def _getobj(self): + """ + Import and return the module object. + + Returns: + The imported module + """ + + def _register_setup_module_fixture(self) -> None: + """Register setup/teardown module fixtures.""" + + def _register_setup_function_fixture(self) -> None: + """Register setup/teardown function fixtures.""" + +class Package(nodes.Directory): + """Collector for Python packages (directories with __init__.py).""" + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + """ + Collect modules and subpackages. + + Returns: + Iterable of Module and Package collectors + """ + + def setup(self) -> None: + """Set up package-level fixtures.""" + +class Class(PyCollector): + """Collector for test methods (and nested classes) in Python classes.""" + + def collect(self) -> Iterable[nodes.Item | nodes.Collector]: + """ + Collect test methods from class. + + Returns: + Iterable of Function items and nested Class collectors + """ + + def newinstance(self): + """ + Create new instance of the class. + + Returns: + New instance of the test class + """ + + @classmethod + def from_parent(cls, parent, *, name, obj=None, **kw) -> Self: + """ + Create Class collector from parent. + + Parameters: + - parent: Parent collector + - name: Class name + - obj: Class object (optional) + + Returns: + Class collector instance + """ + + def _register_setup_class_fixture(self) -> None: + """Register class-level setup/teardown fixtures.""" + + def _register_setup_method_fixture(self) -> None: + """Register method-level setup/teardown fixtures.""" + +class Function(Item): + """Item for Python test functions.""" + + # Core attributes + function: Callable # Test function object + fixturenames: frozenset[str] # Required fixture names + callspec: CallSpec2 | None # Parametrization info + + def runtest(self) -> None: + """Execute the test function.""" + + def setup(self) -> None: + """Set up fixtures and test environment.""" + + def teardown(self) -> None: + """Tear down fixtures and clean up.""" +``` + +### Session Management + +Root-level session management for test execution coordination. + +```python { .api } +class Session(Collector): + """Root of collection tree, collects initial paths.""" + + def collect(self) -> list[Node]: + """Collect from initial file paths.""" + + def pytest_runtest_protocol(self, item: Item) -> bool: + """Run test protocol for item.""" + + def pytest_collection_modifyitems(self, items: list[Item]) -> None: + """Modify collected items.""" +``` + +### Test Parametrization + +Metafunc object for parametrizing tests during collection. + +```python { .api } +class Metafunc: + """Used in pytest_generate_tests hook for parametrizing tests.""" + + # Core attributes + function: Callable # Test function + module: Any # Test module + cls: type | None # Test class (if applicable) + config: Config # pytest configuration + definition: FunctionDefinition # Function definition + fixturenames: frozenset[str] # Fixture names used by function + + def parametrize( + self, + argnames: str | list[str] | tuple[str, ...], + argvalues, + *, + indirect: bool | list[str] = False, + ids=None, + scope: str | None = None + ) -> None: + """Parametrize test function.""" + + def addcall(self, **kwargs) -> None: + """Add individual test call (deprecated, use parametrize).""" +``` + +**Usage Example:** + +```python +# conftest.py +def pytest_generate_tests(metafunc): + if "browser" in metafunc.fixturenames: + metafunc.parametrize( + "browser", + ["chrome", "firefox", "safari"], + ids=["Chrome", "Firefox", "Safari"] + ) + + if "database" in metafunc.fixturenames: + metafunc.parametrize( + "database", + [("sqlite", ":memory:"), ("postgresql", "test_db")], + ids=["SQLite", "PostgreSQL"] + ) + +# Test will run 6 times (3 browsers × 2 databases) +def test_web_app(browser, database): + pass +``` + +### Doctest Integration + +Support for running doctests as part of the test suite. + +```python { .api } +class DoctestItem(Item): + """Test item for executing doctests.""" + + def __init__( + self, + name: str, + parent: DoctestTextfile | DoctestModule, + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> None: + """ + Initialize doctest item. + + Parameters: + - name: Item name + - parent: Parent collector (DoctestTextfile or DoctestModule) + - runner: Doctest runner + - dtest: Doctest object + """ + + # Attributes + runner: doctest.DocTestRunner # Doctest runner + dtest: doctest.DocTest # Doctest object + obj = None # No underlying Python object + _fixtureinfo: fixtures.FuncFixtureInfo # Fixture information + fixturenames: frozenset[str] # Required fixture names + + def runtest(self) -> None: + """ + Execute the doctest. + + Runs the doctest using the configured runner and handles + any failures according to pytest conventions. + """ + + def setup(self) -> None: + """Set up doctest fixtures and environment.""" + + def repr_failure(self, excinfo: ExceptionInfo[BaseException]) -> str | TerminalRepr: + """ + Format doctest failures for display. + + Parameters: + - excinfo: Exception information + + Returns: + Formatted failure representation + """ + + def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: + """ + Get doctest location information for reporting. + + Returns: + Tuple of (filename, line_number, doctest_name) + """ + + @classmethod + def from_parent( + cls, + parent: DoctestTextfile | DoctestModule, + *, + name: str, + runner: doctest.DocTestRunner, + dtest: doctest.DocTest, + ) -> Self: + """ + Create DoctestItem from parent collector. + + Parameters: + - parent: Parent collector + - name: Item name + - runner: Doctest runner + - dtest: Doctest object + + Returns: + DoctestItem instance + """ +``` + +## Collection Process + +The collection process follows these steps: + +1. **Start Collection**: Session.collect() begins from initial paths +2. **Directory Collection**: Dir collectors process directories +3. **File Collection**: Module collectors process Python files +4. **Class Collection**: Class collectors process test classes +5. **Function Collection**: Function collectors process test functions +6. **Parametrization**: Metafunc.parametrize() creates multiple test variants +7. **Item Modification**: pytest_collection_modifyitems hook allows final changes + +**Usage Example:** + +```python +# Custom collector example +class YamlFile(pytest.File): + def collect(self): + # Parse YAML file and create test items + raw = yaml.safe_load(self.path.open()) + for name, spec in raw.items(): + yield YamlTest.from_parent(self, name=name, spec=spec) + +class YamlTest(pytest.Item): + def __init__(self, name, parent, spec): + super().__init__(name, parent) + self.spec = spec + + def runtest(self): + # Execute test based on YAML spec + pass + +def pytest_collect_file(parent, path): + if path.suffix == ".yaml" and path.name.startswith("test_"): + return YamlFile.from_parent(parent, path=path) +``` + +## Types + +```python { .api } +from typing import Any, Callable +from pathlib import Path + +class CallSpec2: + """Represents parametrized test call specification.""" + + # Attributes + params: dict[str, Any] # Parameter values + funcargs: dict[str, Any] # Function arguments + id: str | None # Parameter set ID + marks: list[Mark] # Applied marks + + def setmulti(self, **kwargs) -> None: + """Set multiple parameters.""" + +class FunctionDefinition: + """Definition of a test function for collection.""" + + # Attributes + name: str # Function name + obj: Callable # Function object + parent: Node # Parent collector + + def getparent(self, cls: type) -> Node | None: + """Get parent of specific type.""" +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md b/.tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md new file mode 100644 index 000000000..b4fab95d3 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md @@ -0,0 +1,219 @@ +# Test Utilities and Environment Control + +Utilities for controlling test environments including monkeypatching, output capture, temporary paths, and specialized tools for testing pytest plugins. These utilities provide comprehensive control over the test execution environment. + +## Capabilities + +### Environment Modification + +MonkeyPatch provides safe, temporary modifications to objects, environment variables, and system paths. + +```python { .api } +class MonkeyPatch: + """Helper for temporarily modifying objects, environment, and sys.path.""" + + def setattr(self, target, name, value=, raising: bool = True) -> None: + """Set attribute on target object.""" + + def setitem(self, dic, name, value) -> None: + """Set dictionary item.""" + + def setenv(self, name: str, value: str, prepend: str | None = None) -> None: + """Set environment variable.""" + + def syspath_prepend(self, path) -> None: + """Prepend to sys.path.""" + + def chdir(self, path) -> None: + """Change current working directory.""" + + def undo(self) -> None: + """Undo all changes.""" +``` + +**Usage Example:** + +```python +def test_monkeypatch(monkeypatch): + # Modify object attribute + monkeypatch.setattr("os.getcwd", lambda: "/fake/path") + + # Set environment variable + monkeypatch.setenv("TEST_MODE", "true") + + # Modify dictionary + import sys + monkeypatch.setitem(sys.modules, "fake_module", FakeModule()) + + # Change directory + monkeypatch.chdir("/tmp") + + # All changes are automatically undone after test +``` + +### Output Capture + +Capture stdout, stderr, and file descriptor output during test execution. + +```python { .api } +class CaptureFixture: + """Captures stdout/stderr output during tests.""" + + def readouterr(self) -> CaptureResult: + """Read and return captured output.""" + + def disabled(self): + """Context manager to temporarily disable capturing.""" + +class CaptureResult: + """Result of captured output.""" + out: str # Captured stdout + err: str # Captured stderr +``` + +**Usage Example:** + +```python +def test_output_capture(capsys): + print("Hello stdout") + print("Hello stderr", file=sys.stderr) + + captured = capsys.readouterr() + assert captured.out == "Hello stdout\\n" + assert captured.err == "Hello stderr\\n" + + # Temporarily disable capture + with capsys.disabled(): + print("This will be printed normally") + +# Different capture fixtures available: +def test_capture_variants(capsys, capsysbinary, capfd, capfdbinary): + # capsys: capture sys.stdout/stderr (text) + # capsysbinary: capture sys.stdout/stderr (binary) + # capfd: capture file descriptors 1/2 (text) + # capfdbinary: capture file descriptors 1/2 (binary) + pass +``` + +### Log Capture + +Capture and inspect log messages during test execution. + +```python { .api } +class LogCaptureFixture: + """Provides access and control of log capturing.""" + + # Attributes + handler: LogCaptureHandler # Log handler + records: list[logging.LogRecord] # Captured log records + + def get_records(self, when: str) -> list[logging.LogRecord]: + """Get log records for specific test phase.""" + + def clear(self) -> None: + """Clear captured records.""" + + def set_level(self, level: int | str, logger: str | None = None) -> None: + """Set logging level.""" +``` + +**Usage Example:** + +```python +import logging + +def test_logging(caplog): + with caplog.at_level(logging.INFO): + logging.info("This is an info message") + logging.warning("This is a warning") + + assert len(caplog.records) == 2 + assert caplog.records[0].levelname == "INFO" + assert "info message" in caplog.records[0].message + + # Clear records + caplog.clear() + assert len(caplog.records) == 0 + + # Check specific logger + logger = logging.getLogger("myapp") + logger.error("Application error") + + assert any("Application error" in record.message + for record in caplog.records) +``` + +### Temporary Paths + +Factory for creating temporary directories and files. + +```python { .api } +class TempPathFactory: + """Factory for creating temporary directories.""" + + def mktemp(self, basename: str, numbered: bool = True) -> Path: + """Create temporary directory.""" + + def getbasetemp(self) -> Path: + """Get base temporary directory.""" + +# Built-in fixtures +def test_temp_paths(tmp_path, tmp_path_factory): + # tmp_path: pathlib.Path to temporary directory (function scope) + # tmp_path_factory: TempPathFactory instance + + # Create file in temporary directory + file_path = tmp_path / "test_file.txt" + file_path.write_text("test content") + assert file_path.read_text() == "test content" + + # Create additional temp directory + another_temp = tmp_path_factory.mktemp("custom_dir") + assert another_temp.exists() +``` + +### Plugin Testing Framework + +Comprehensive tools for testing pytest plugins and functionality. + +```python { .api } +class Pytester: + """Facilities for testing pytest plugins and functionality.""" + + def makepyfile(self, **kwargs) -> Path: + """Create Python file with content.""" + + def makeconftest(self, source: str) -> Path: + """Create conftest.py file.""" + + def makefile(self, ext: str, **kwargs) -> Path: + """Create file with given extension.""" + + def mkdir(self, name: str) -> Path: + """Create directory.""" + + def runpytest(self, *args, **kwargs) -> RunResult: + """Run pytest in subprocess.""" + + def runpytest_subprocess(self, *args, **kwargs) -> RunResult: + """Run pytest in subprocess with isolation.""" + + def runpytest_inprocess(self, *args, **kwargs) -> RunResult: + """Run pytest in same process.""" + +class RunResult: + """Result of running pytest in subprocess.""" + + # Attributes + ret: int # Return code + outlines: list[str] # stdout lines + errlines: list[str] # stderr lines + stdout: str # Full stdout + stderr: str # Full stderr + duration: float # Execution duration + + def parseoutcomes(self) -> dict[str, int]: + """Parse test outcomes from output.""" + + def assert_outcomes(self, **expected) -> None: + \"\"\"Assert expected test outcomes.\"\"\"\n\nclass HookRecorder:\n \"\"\"Records hook calls for testing.\"\"\"\n \n def getcalls(self, names: str | list[str]) -> list[RecordedHookCall]:\n \"\"\"Get recorded calls for hook names.\"\"\"\n \n def assert_contains(self, entries) -> None:\n \"\"\"Assert recorder contains specific entries.\"\"\"\n \n def pop(self, name: str) -> RecordedHookCall:\n \"\"\"Remove and return last call for hook name.\"\"\"\n\nclass RecordedHookCall:\n \"\"\"Represents a recorded hook call.\"\"\"\n pass\n\nclass LineMatcher:\n \"\"\"Flexible matching of text output lines.\"\"\"\n \n def fnmatch_lines(self, lines2: list[str]) -> None:\n \"\"\"Assert lines match using fnmatch patterns.\"\"\"\n \n def re_match_lines(self, lines2: list[str]) -> None:\n \"\"\"Assert lines match using regex patterns.\"\"\"\n \n def no_fnmatch_line(self, pat: str) -> None:\n \"\"\"Assert no line matches fnmatch pattern.\"\"\"\n```\n\n**Usage Example:**\n\n```python\ndef test_my_plugin(pytester):\n # Create test files\n pytester.makepyfile(\"\"\"\n def test_example():\n assert True\n \"\"\")\n \n pytester.makeconftest(\"\"\"\n import pytest\n \n @pytest.fixture\n def my_fixture():\n return \"test_value\"\n \"\"\")\n \n # Run pytest\n result = pytester.runpytest(\"-v\")\n \n # Check results\n result.assert_outcomes(passed=1)\n assert result.ret == 0\n \n # Check output\n result.stdout.fnmatch_lines([\n \"*test_example PASSED*\"\n ])\n\ndef test_hook_recording(pytester):\n pytester.makepyfile(\"\"\"\n def test_foo(): pass\n \"\"\")\n \n # Record hooks\n rec = pytester.runpytest(\"--collect-only\")\n \n # Check hook calls\n calls = rec.getcalls(\"pytest_collection_modifyitems\")\n assert len(calls) == 1\n```\n\n### Legacy Path Support (Deprecated)\n\nLegacy support for py.path and testdir (deprecated in favor of pathlib.Path).\n\n```python { .api }\nclass TempdirFactory:\n \"\"\"Legacy factory for temporary directories (deprecated).\"\"\"\n pass\n\nclass Testdir:\n \"\"\"Legacy test directory helper (deprecated).\"\"\"\n pass\n```\n\n## Built-in Utility Fixtures\n\nSummary of built-in fixtures for test utilities:\n\n```python\ndef test_with_all_utilities(\n monkeypatch, # MonkeyPatch instance\n capsys, # Capture sys.stdout/stderr (text)\n capsysbinary, # Capture sys.stdout/stderr (binary)\n capfd, # Capture file descriptors (text)\n capfdbinary, # Capture file descriptors (binary) \n caplog, # LogCaptureFixture instance\n tmp_path, # pathlib.Path to temp directory\n tmp_path_factory, # TempPathFactory instance\n pytester, # Pytester instance (for plugin testing)\n recwarn, # WarningsRecorder instance\n # Legacy (deprecated)\n tmpdir, # py.path.local temp directory\n tmpdir_factory, # TempdirFactory instance\n testdir # Testdir instance\n):\n pass\n``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md b/.tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md new file mode 100644 index 000000000..e46dfbe05 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md @@ -0,0 +1,189 @@ +# Testing Functions + +Core functions for controlling test execution flow and outcomes. These functions provide the fundamental building blocks for managing test behavior, conditional execution, and early termination scenarios. + +## Capabilities + +### Test Skipping + +Skip test execution with optional reason and support for module-level skipping. + +```python { .api } +def skip(reason: str = "", *, allow_module_level: bool = False): + """ + Imperatively skip a test with optional reason. + + Parameters: + - reason: Explanation for why the test is skipped + - allow_module_level: Whether to allow skipping at module level (outside test functions) + + Raises: + pytest.skip.Exception (Skipped) + """ +``` + +**Usage Example:** + +```python +import pytest + +def test_conditional_skip(): + if not has_network(): + pytest.skip("Network not available") + # test code here + +# Module-level skip +if not sys.platform.startswith("linux"): + pytest.skip("Linux-only tests", allow_module_level=True) +``` + +### Expected Failures + +Mark tests as expected to fail with optional reason. + +```python { .api } +def xfail(reason: str = ""): + """ + Mark test as expected to fail. + + Parameters: + - reason: Explanation for why the test is expected to fail + + Raises: + pytest.xfail.Exception (XFailed) + """ +``` + +**Usage Example:** + +```python +import pytest + +def test_known_bug(): + if bug_exists(): + pytest.xfail("Bug #123 not fixed yet") + assert complex_calculation() == expected_result +``` + +### Test Failures + +Explicitly fail tests with custom messages and traceback control. + +```python { .api } +def fail(reason: str = "", pytrace: bool = True): + """ + Explicitly fail a test with message and traceback control. + + Parameters: + - reason: Failure message to display + - pytrace: Whether to show pytest traceback (True) or just the message (False) + + Raises: + pytest.fail.Exception (Failed) + """ +``` + +**Usage Example:** + +```python +import pytest + +def test_validation(): + result = validate_data(data) + if not result.is_valid: + pytest.fail(f"Validation failed: {result.errors}") +``` + +### Test Suite Exit + +Exit the entire test suite immediately with reason and exit code. + +```python { .api } +def exit(reason: str = "", returncode: int | None = None): + """ + Exit testing process immediately. + + Parameters: + - reason: Explanation for early exit + - returncode: Exit code to use (defaults to appropriate pytest exit code) + + Raises: + pytest.exit.Exception (Exit) + """ +``` + +**Usage Example:** + +```python +import pytest + +def test_critical_setup(): + if not critical_system_available(): + pytest.exit("Critical system unavailable, cannot continue testing") +``` + +### Conditional Imports + +Import modules or skip tests if import fails, with version checking support. + +```python { .api } +def importorskip( + modname: str, + minversion: str | None = None, + reason: str | None = None, + *, + exc_type: type[ImportError] | None = None +): + """ + Import module or skip test if import fails. + + Parameters: + - modname: Name of module to import + - minversion: Minimum required version (if module has __version__) + - reason: Custom skip reason if import/version check fails + - exc_type: Specific exception type to catch (defaults to ImportError) + + Returns: + The imported module + + Raises: + pytest.skip.Exception if import fails or version insufficient + """ +``` + +**Usage Example:** + +```python +import pytest + +# Skip test if numpy not available +numpy = pytest.importorskip("numpy") + +# Skip if version too old +requests = pytest.importorskip("requests", minversion="2.20.0") + +def test_with_numpy(): + array = numpy.array([1, 2, 3]) + assert len(array) == 3 + +def test_with_requests(): + response = requests.get("https://httpbin.org/get") + assert response.status_code == 200 +``` + +## Exception Types + +```python { .api } +# These are the exceptions raised by the testing functions +class skip.Exception(Exception): + """Raised by pytest.skip()""" + +class xfail.Exception(Exception): + """Raised by pytest.xfail()""" + +class fail.Exception(Exception): + """Raised by pytest.fail()""" + +class exit.Exception(Exception): + """Raised by pytest.exit()""" +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/warnings.md b/.tessl/tiles/tessl/pypi-pytest/docs/warnings.md new file mode 100644 index 000000000..f6d3236f2 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/docs/warnings.md @@ -0,0 +1,341 @@ +# Warning Types and Error Handling + +Comprehensive warning type hierarchy for pytest-specific warnings including deprecation warnings, configuration warnings, collection warnings, and error handling patterns. These warnings help developers identify potential issues and deprecated functionality. + +## Capabilities + +### Base Warning Types + +Foundation warning classes for the pytest warning hierarchy. + +```python { .api } +class PytestWarning(UserWarning): + """ + Base class for all pytest warnings. + + This is the base class that all pytest-specific warnings inherit from. + It extends Python's UserWarning to provide pytest-specific warning behavior. + """ + +class PytestDeprecationWarning(PytestWarning, DeprecationWarning): + """ + Warning for features that will be removed in future versions. + + Used to warn about deprecated pytest functionality that will be removed + in upcoming major releases. Extends both PytestWarning and DeprecationWarning. + """ +``` + +### Specific Warning Categories + +Detailed warning types for different aspects of pytest functionality. + +```python { .api } +class PytestAssertRewriteWarning(PytestWarning): + """ + Warnings from the assert rewrite module. + + Issued when there are problems with assertion rewriting, such as: + - Files that cannot be rewritten + - Import-time assertion rewrite issues + - Conflicting assertion rewrite configurations + """ + +class PytestCacheWarning(PytestWarning): + """ + Cache plugin warnings. + + Issued when there are problems with pytest's caching functionality: + - Cache directory access issues + - Cache corruption problems + - Cache configuration warnings + """ + +class PytestCollectionWarning(PytestWarning): + """ + Collection failures for files or symbols. + + Issued during test collection when: + - Files cannot be imported + - Test functions/classes cannot be collected + - Collection configuration issues arise + """ + +class PytestConfigWarning(PytestWarning): + """ + Configuration issues and warnings. + + Issued when there are problems with pytest configuration: + - Invalid configuration values + - Conflicting configuration options + - Missing required configuration + """ + +class PytestUnknownMarkWarning(PytestWarning): + """ + Unknown marker usage warnings. + + Issued when tests use marks that haven't been registered: + - Unregistered custom marks + - Typos in mark names + - Missing mark definitions + """ +``` + +### Experimental and Future Warnings + +Warnings for experimental features and future changes. + +```python { .api } +class PytestExperimentalApiWarning(PytestWarning, FutureWarning): + """ + Experimental API usage warnings. + + Issued when using experimental pytest APIs that: + - May change without notice + - Are not covered by backward compatibility guarantees + - Are in development and may be removed + """ + +class PytestRemovedIn9Warning(PytestDeprecationWarning): + """ + Features that will be removed in pytest 9. + + Specific deprecation warning for functionality scheduled for removal + in pytest version 9.0. Provides clear migration path and timeline. + """ +``` + +### Runtime and Execution Warnings + +Warnings related to test execution and runtime behavior. + +```python { .api } +class PytestReturnNotNoneWarning(PytestWarning): + """ + Test functions returning non-None values. + + Issued when test functions return values other than None: + - Test functions that return values + - Generator functions used as tests incorrectly + - Functions that should be fixtures but aren't marked + """ + +class PytestUnraisableExceptionWarning(PytestWarning): + """ + Unraisable exceptions (e.g., in __del__ methods). + + Issued when exceptions occur that cannot be properly raised: + - Exceptions in __del__ methods + - Exceptions in cleanup code + - Exceptions in background threads + """ + +class PytestUnhandledThreadExceptionWarning(PytestWarning): + """ + Unhandled exceptions in threads. + + Issued when exceptions occur in threads that are not properly handled: + - Background thread exceptions + - Thread pool exceptions + - Daemon thread exceptions + """ + +class PytestFDWarning(PytestWarning): + """ + File descriptor leak warnings. + + Issued by the lsof plugin when file descriptor leaks are detected: + - Open files not properly closed + - Network connections left open + - Resource leaks between tests + """ +``` + +## Warning Usage Examples + +### Filtering Warnings + +```python +import pytest +import warnings + +# Filter warnings in test code +def test_with_warning_filter(): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", PytestDeprecationWarning) + # Code that would generate deprecation warnings + deprecated_function() + +# Using pytest.mark.filterwarnings +@pytest.mark.filterwarnings("ignore::pytest.PytestDeprecationWarning") +def test_ignore_deprecation(): + deprecated_function() + +@pytest.mark.filterwarnings("error::pytest.PytestUnknownMarkWarning") +def test_treat_unknown_marks_as_errors(): + pass +``` + +### Configuration-based Warning Control + +```python +# pytest.ini +[tool:pytest] +filterwarnings = + error + ignore::pytest.PytestDeprecationWarning + ignore::DeprecationWarning:django.* + default::pytest.PytestExperimentalApiWarning + +# pyproject.toml +[tool.pytest.ini_options] +filterwarnings = [ + "error", + "ignore::pytest.PytestDeprecationWarning", + "default::pytest.PytestExperimentalApiWarning", +] +``` + +### Custom Warning Handling + +```python +import pytest +import warnings + +def test_custom_warning_handling(recwarn): + # Trigger some pytest warnings + warnings.warn("Test warning", PytestConfigWarning) + warnings.warn("Another warning", PytestCollectionWarning) + + # Check that warnings were issued + assert len(recwarn) == 2 + assert recwarn[0].category == PytestConfigWarning + assert recwarn[1].category == PytestCollectionWarning + + # Check warning messages + assert "Test warning" in str(recwarn[0].message) + assert "Another warning" in str(recwarn[1].message) + +def test_specific_warning_check(): + with pytest.warns(PytestDeprecationWarning, match="deprecated"): + warnings.warn("This feature is deprecated", PytestDeprecationWarning) +``` + +## Common Warning Scenarios + +### Unknown Marks + +```python +# This will generate PytestUnknownMarkWarning +@pytest.mark.unknownmark # Typo or unregistered mark +def test_with_unknown_mark(): + pass + +# Fix by registering the mark +# pytest.ini +[tool:pytest] +markers = + unknownmark: description of the mark +``` + +### Return Values in Tests + +```python +# This will generate PytestReturnNotNoneWarning +def test_returning_value(): + return "Should not return anything" # Warning issued + +# Fix by not returning values +def test_fixed(): + result = calculate_something() + assert result == expected # Don't return the result +``` + +### Deprecated Features + +```python +# Using deprecated pytest features +def test_with_deprecated_feature(): + # This might generate PytestDeprecationWarning or PytestRemovedIn9Warning + pytest.deprecated_function() # Replace with newer API + +# Check for specific deprecation warnings +def test_handle_deprecation(): + with pytest.warns(PytestRemovedIn9Warning): + use_deprecated_feature() +``` + +### Configuration Issues + +```python +# pytest.ini with conflicting options might generate PytestConfigWarning +[tool:pytest] +addopts = --strict-markers +markers = + # Missing marker definitions can cause warnings + +# Fix by properly defining all markers +markers = + slow: marks tests as slow + integration: marks tests as integration tests +``` + +## Warning Hierarchy + +The complete warning hierarchy: + +``` +UserWarning +├── PytestWarning (base for all pytest warnings) + ├── PytestAssertRewriteWarning + ├── PytestCacheWarning + ├── PytestCollectionWarning + ├── PytestConfigWarning + ├── PytestUnknownMarkWarning + ├── PytestReturnNotNoneWarning + ├── PytestUnraisableExceptionWarning + ├── PytestUnhandledThreadExceptionWarning + ├── PytestFDWarning + ├── PytestDeprecationWarning (also extends DeprecationWarning) + │ └── PytestRemovedIn9Warning + └── PytestExperimentalApiWarning (also extends FutureWarning) +``` + +## Best Practices + +### Handling Warnings in Tests + +```python +# Don't ignore all warnings - be specific +@pytest.mark.filterwarnings("ignore::DeprecationWarning:specific_module.*") + +# Test that warnings are properly issued +def test_warning_issued(): + with pytest.warns(PytestDeprecationWarning): + deprecated_api() + +# Handle warnings in fixtures +@pytest.fixture +def ignore_known_warnings(): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", PytestConfigWarning) + yield +``` + +### Warning Documentation + +```python +def deprecated_function(): + """ + This function is deprecated. + + .. deprecated:: 8.0 + Use new_function() instead. Will be removed in pytest 9.0. + """ + warnings.warn( + "deprecated_function is deprecated, use new_function instead", + PytestRemovedIn9Warning, + stacklevel=2 + ) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/tile.json b/.tessl/tiles/tessl/pypi-pytest/tile.json new file mode 100644 index 000000000..fe31228f4 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-pytest/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-pytest", + "version": "8.4.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/pytest@8.4.2", + "summary": "pytest: simple powerful testing with Python" +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md new file mode 100644 index 000000000..c565e37aa --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md @@ -0,0 +1,288 @@ +# Command Line Interface + +Complete command-line tool for managing .env files with list, get, set, unset, and run commands supporting multiple output formats. + +## Installation + +```bash +pip install "python-dotenv[cli]" +``` + +## Global Options + +All commands support these global options: + +```bash { .api } +dotenv [OPTIONS] COMMAND [ARGS]... + +Options: + -f, --file PATH Location of the .env file, defaults to .env file in current working directory + -q, --quote [always|never|auto] Whether to quote or not the variable values. Default mode is always + -e, --export Whether to write the dot file as an executable bash script + --version Show the version and exit + --help Show help message and exit +``` + +## Capabilities + +### Listing Variables + +Display all stored key/value pairs with support for multiple output formats. + +```bash { .api } +dotenv list [OPTIONS] + +Options: + --format [simple|json|shell|export] The format in which to display the list (default: simple) +``` + +Usage examples: + +```bash +# List all variables in simple format +dotenv list + +# List in JSON format +dotenv list --format=json + +# List in shell format (with proper quoting) +dotenv list --format=shell + +# List in export format (prefixed with 'export') +dotenv list --format=export + +# List from custom file +dotenv -f /path/to/custom.env list +``` + +Output examples: + +```bash +# Simple format +DATABASE_URL=postgresql://localhost/mydb +DEBUG=True +API_KEY=secret123 + +# JSON format +{ + "API_KEY": "secret123", + "DATABASE_URL": "postgresql://localhost/mydb", + "DEBUG": "True" +} + +# Shell format (properly quoted) +DATABASE_URL='postgresql://localhost/mydb' +DEBUG=True +API_KEY=secret123 + +# Export format +export DATABASE_URL='postgresql://localhost/mydb' +export DEBUG=True +export API_KEY=secret123 +``` + +### Getting Values + +Retrieve the value for a specific key. + +```bash { .api } +dotenv get KEY +``` + +Usage examples: + +```bash +# Get a specific value +dotenv get DATABASE_URL + +# Get from custom file +dotenv -f production.env get API_KEY + +# Handle missing keys (exits with code 1) +dotenv get NONEXISTENT_KEY +echo $? # Returns 1 +``` + +### Setting Values + +Store key/value pairs in the .env file. + +```bash { .api } +dotenv set KEY VALUE +``` + +Usage examples: + +```bash +# Set a basic key/value pair +dotenv set DATABASE_URL "postgresql://localhost/mydb" + +# Set with different quote modes +dotenv -q never set SIMPLE_KEY simple_value +dotenv -q auto set SPACED_KEY "value with spaces" +dotenv -q always set QUOTED_KEY always_quoted + +# Set with export prefix +dotenv -e set PATH_ADDITION "/usr/local/bin" + +# Set in custom file +dotenv -f production.env set API_KEY "prod-secret-123" + +# Set complex values +dotenv set JSON_CONFIG '{"host": "localhost", "port": 5432}' +dotenv set MULTILINE_VALUE "line1 +line2 +line3" +``` + +### Removing Values + +Remove keys from the .env file. + +```bash { .api } +dotenv unset KEY +``` + +Usage examples: + +```bash +# Remove a key +dotenv unset OLD_CONFIG + +# Remove from custom file +dotenv -f staging.env unset TEMP_KEY + +# Handle missing keys (exits with code 1) +dotenv unset NONEXISTENT_KEY +echo $? # Returns 1 +``` + +### Running Commands + +Execute commands with environment variables loaded from the .env file. + +```bash { .api } +dotenv run [OPTIONS] COMMAND [ARGS]... + +Options: + --override/--no-override Override variables from the environment file with those from the .env file (default: --override) +``` + +Usage examples: + +```bash +# Run a Python script with .env variables +dotenv run python app.py + +# Run with arguments +dotenv run python manage.py migrate + +# Run from custom .env file +dotenv -f production.env run python app.py + +# Don't override existing environment variables +dotenv run --no-override python app.py + +# Run complex commands +dotenv run sh -c 'echo "Database: $DATABASE_URL"' + +# Run commands that need specific environment +dotenv run pytest tests/ +dotenv run npm start +dotenv run make build +``` + +The `run` command: +- Loads variables from the .env file +- Merges them with existing environment variables +- Executes the specified command with the combined environment +- **With `--override` (default)**: .env file variables take precedence over existing environment variables +- **With `--no-override`**: existing environment variables take precedence over .env file variables +- Variables that exist only in .env file or only in environment are always included + +## File Selection + +Use the `-f/--file` option to specify a custom .env file: + +```bash +# Different .env files for different environments +dotenv -f .env.development list +dotenv -f .env.staging set API_URL "https://staging-api.example.com" +dotenv -f .env.production run python deploy.py + +# Absolute paths +dotenv -f /etc/myapp/.env list + +# Relative paths +dotenv -f ../shared.env get SHARED_KEY +``` + +## Quote Modes + +Control how values are quoted using the `-q/--quote` option: + +```bash +# Always quote (default) +dotenv -q always set KEY "value" # KEY='value' + +# Never quote +dotenv -q never set KEY value # KEY=value + +# Auto quote (quotes only when necessary) +dotenv -q auto set SIMPLE abc # SIMPLE=abc +dotenv -q auto set COMPLEX "a b" # COMPLEX='a b' +``` + +## Export Mode + +Generate bash-compatible output using the `-e/--export` option: + +```bash +# Set with export prefix +dotenv -e set PATH_VAR "/usr/local/bin" +# Results in: export PATH_VAR='/usr/local/bin' + +# List with export format +dotenv -e list --format=export +# Shows all variables prefixed with 'export' + +# Create sourceable .env file +dotenv -e -f bash-compatible.env set DATABASE_URL "postgresql://localhost/mydb" +# Now you can: source bash-compatible.env +``` + +## Exit Codes + +The CLI tool uses standard exit codes: + +- **0**: Success +- **1**: Command failed (key not found, file doesn't exist, etc.) +- **2**: File access error (permissions, file not readable, etc.) + +## Examples + +```bash +# Complete workflow example +cd myproject + +# Initialize with some variables +dotenv set DATABASE_URL "postgresql://localhost/mydb" +dotenv set DEBUG "True" +dotenv set SECRET_KEY "dev-secret-key" + +# List all variables +dotenv list + +# Get specific value +echo "Database: $(dotenv get DATABASE_URL)" + +# Run application with environment +dotenv run python app.py + +# Export for bash sourcing +dotenv -e list --format=export > environment.sh +source environment.sh + +# Clean up +dotenv unset SECRET_KEY +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md new file mode 100644 index 000000000..6aa3599d4 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md @@ -0,0 +1,202 @@ +# Environment Loading + +Core functionality for loading environment variables from .env files with automatic file discovery, variable expansion, and flexible configuration options. + +## Capabilities + +### Loading Environment Variables + +Load environment variables from .env files directly into the system environment, with support for override control and variable interpolation. + +```python { .api } +def load_dotenv( + dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + override: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8" +) -> bool: + """ + Parse a .env file and then load all the variables found as environment variables. + + Parameters: + dotenv_path: Absolute or relative path to .env file + stream: Text stream (such as io.StringIO) with .env content, used if dotenv_path is None + verbose: Whether to output a warning if the .env file is missing + override: Whether to override the system environment variables with variables from the .env file + interpolate: Whether to interpolate variables using POSIX variable expansion + encoding: Encoding to be used to read the file + + Returns: + bool: True if at least one environment variable is set, else False + """ +``` + +Usage examples: + +```python +from dotenv import load_dotenv +import os + +# Load from default .env file +load_dotenv() + +# Load from specific file +load_dotenv('/path/to/custom.env') + +# Override existing environment variables +load_dotenv(override=True) + +# Load from string stream +from io import StringIO +config = StringIO("DATABASE_URL=sqlite:///app.db\nDEBUG=True") +load_dotenv(stream=config) + +# Disable variable interpolation +load_dotenv(interpolate=False) +``` + +### Parsing Without Environment Modification + +Parse .env files and return their contents as a dictionary without modifying the system environment. + +```python { .api } +def dotenv_values( + dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8" +) -> Dict[str, Optional[str]]: + """ + Parse a .env file and return its content as a dict. + + The returned dict will have None values for keys without values in the .env file. + For example, 'foo=bar' results in {"foo": "bar"} whereas 'foo' alone results in {"foo": None} + + Parameters: + dotenv_path: Absolute or relative path to the .env file + stream: StringIO object with .env content, used if dotenv_path is None + verbose: Whether to output a warning if the .env file is missing + interpolate: Whether to interpolate variables using POSIX variable expansion + encoding: Encoding to be used to read the file + + Returns: + dict: Dictionary with keys and values from the .env file + """ +``` + +Usage examples: + +```python +from dotenv import dotenv_values +import os + +# Parse .env file to dictionary +config = dotenv_values(".env") +database_url = config.get('DATABASE_URL') + +# Advanced configuration management +config = { + **dotenv_values(".env.shared"), # load shared development variables + **dotenv_values(".env.secret"), # load sensitive variables + **os.environ, # override loaded values with environment variables +} + +# Parse from stream +from io import StringIO +env_stream = StringIO("USER=foo\nEMAIL=foo@example.org") +config = dotenv_values(stream=env_stream) +# Returns: {"USER": "foo", "EMAIL": "foo@example.org"} +``` + +### File Discovery + +Automatically discover .env files by searching in increasingly higher folders from the current directory or script location. + +```python { .api } +def find_dotenv( + filename: str = ".env", + raise_error_if_not_found: bool = False, + usecwd: bool = False +) -> str: + """ + Search in increasingly higher folders for the given file. + + Parameters: + filename: Name of the file to search for (default: ".env") + raise_error_if_not_found: Whether to raise IOError if file is not found + usecwd: Whether to start search from current working directory instead of script location + + Returns: + str: Path to the file if found, or an empty string otherwise + + Raises: + IOError: If raise_error_if_not_found is True and file is not found + """ +``` + +Usage examples: + +```python +from dotenv import find_dotenv, load_dotenv + +# Find .env file automatically +env_path = find_dotenv() +if env_path: + load_dotenv(env_path) + +# Search for custom filename +config_path = find_dotenv('.config') + +# Raise error if not found +try: + env_path = find_dotenv(raise_error_if_not_found=True) + load_dotenv(env_path) +except IOError: + print("No .env file found") + +# Search from current working directory +env_path = find_dotenv(usecwd=True) +``` + +## Variable Expansion + +python-dotenv supports POSIX-style variable expansion using `${VAR}` syntax with optional default values: + +```bash +# .env file example +DOMAIN=example.org +ADMIN_EMAIL=admin@${DOMAIN} +ROOT_URL=${DOMAIN}/app +API_KEY=${SECRET_KEY:-default-key} + +# More complex examples +DATABASE_HOST=localhost +DATABASE_PORT=5432 +DATABASE_NAME=myapp +DATABASE_URL=postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME} + +# Using defaults for optional configuration +REDIS_URL=${REDIS_HOST:-localhost}:${REDIS_PORT:-6379} +LOG_LEVEL=${DEBUG:-false} +MAX_WORKERS=${WORKER_COUNT:-4} + +# Nested variable expansion +APP_ENV=production +CONFIG_FILE=config.${APP_ENV}.json +CONFIG_PATH=/etc/myapp/${CONFIG_FILE} +``` + +With `load_dotenv(override=True)` or `dotenv_values()`, variable resolution follows this priority: +1. Value of that variable in the .env file +2. Value of that variable in the environment +3. Default value (if provided with `:-` syntax) +4. Empty string + +With `load_dotenv(override=False)`, the priority is: +1. Value of that variable in the environment +2. Value of that variable in the .env file +3. Default value (if provided with `:-` syntax) +4. Empty string \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md new file mode 100644 index 000000000..a677e444d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md @@ -0,0 +1,232 @@ +# File Manipulation + +Programmatic functions for reading, writing, and modifying .env files with support for different quote modes and export formats. + +## Capabilities + +### Reading Values + +Retrieve the value of a specific key from a .env file without loading all variables. + +```python { .api } +def get_key( + dotenv_path: Union[str, os.PathLike[str]], + key_to_get: str, + encoding: Optional[str] = "utf-8" +) -> Optional[str]: + """ + Get the value of a given key from the given .env file. + + Parameters: + dotenv_path: Path to the .env file + key_to_get: The key to retrieve + encoding: File encoding (default: "utf-8") + + Returns: + str or None: Value of the key if found, None if key doesn't exist or has no value + """ +``` + +Usage examples: + +```python +from dotenv import get_key + +# Get a specific value +database_url = get_key('.env', 'DATABASE_URL') +if database_url: + print(f"Database URL: {database_url}") + +# Handle missing keys +api_key = get_key('.env', 'API_KEY') +if api_key is None: + print("API_KEY not found in .env file") +``` + +### Setting Values + +Add or update key/value pairs in .env files with support for different quoting modes and export formats. + +```python { .api } +def set_key( + dotenv_path: Union[str, os.PathLike[str]], + key_to_set: str, + value_to_set: str, + quote_mode: str = "always", + export: bool = False, + encoding: Optional[str] = "utf-8" +) -> Tuple[Optional[bool], str, str]: + """ + Add or update a key/value to the given .env file. + + If the .env path given doesn't exist, fails instead of risking creating + an orphan .env somewhere in the filesystem. + + Parameters: + dotenv_path: Path to the .env file + key_to_set: The key to set + value_to_set: The value to set + quote_mode: Quote mode - "always", "auto", or "never" + export: Whether to prefix the line with "export " + encoding: File encoding (default: "utf-8") + + Returns: + tuple: (success_bool, key, value) where success_bool indicates if operation succeeded + + Raises: + ValueError: If quote_mode is not one of "always", "auto", "never" + """ +``` + +Usage examples: + +```python +from dotenv import set_key + +# Set a basic key/value pair +success, key, value = set_key('.env', 'DATABASE_URL', 'postgresql://localhost/mydb') +if success: + print(f"Set {key}={value}") + +# Set with different quote modes +set_key('.env', 'SIMPLE_KEY', 'simple_value', quote_mode='never') # SIMPLE_KEY=simple_value +set_key('.env', 'SPACED_KEY', 'value with spaces', quote_mode='auto') # SPACED_KEY='value with spaces' +set_key('.env', 'QUOTED_KEY', 'always_quoted', quote_mode='always') # QUOTED_KEY='always_quoted' + +# Set with export prefix for bash compatibility +set_key('.env', 'PATH_VAR', '/usr/local/bin', export=True) +# Results in: export PATH_VAR='/usr/local/bin' + +# Handle special characters +set_key('.env', 'PASSWORD', "p@ssw0rd'with\"quotes", quote_mode='always') +``` + +### Removing Values + +Remove keys and their values from .env files. + +```python { .api } +def unset_key( + dotenv_path: Union[str, os.PathLike[str]], + key_to_unset: str, + quote_mode: str = "always", + encoding: Optional[str] = "utf-8" +) -> Tuple[Optional[bool], str]: + """ + Remove a given key from the given .env file. + + Parameters: + dotenv_path: Path to the .env file + key_to_unset: The key to remove + quote_mode: Quote mode for file operations (currently unused but kept for consistency) + encoding: File encoding (default: "utf-8") + + Returns: + tuple: (success_bool, key) where success_bool is True if key was removed, + None if file doesn't exist or key wasn't found + + Raises: + Warning logged if file doesn't exist or key doesn't exist + """ +``` + +Usage examples: + +```python +from dotenv import unset_key + +# Remove a key +success, key = unset_key('.env', 'OLD_CONFIG_KEY') +if success: + print(f"Removed {key} from .env file") +elif success is None: + print("Key not found or file doesn't exist") + +# Remove multiple keys +keys_to_remove = ['TEMP_KEY', 'DEPRECATED_SETTING', 'OLD_API_KEY'] +for key in keys_to_remove: + success, removed_key = unset_key('.env', key) + if success: + print(f"Removed {removed_key}") +``` + +## Quote Modes + +The `quote_mode` parameter controls how values are quoted in the .env file: + +- **"always"** (default): All values are wrapped in single quotes +- **"auto"**: Values containing non-alphanumeric characters are quoted, simple values are not +- **"never"**: Values are never quoted (use with caution for values with spaces or special characters) + +Examples: + +```python +from dotenv import set_key + +# Different quote modes with the same value +value = "hello world" + +set_key('.env', 'KEY1', value, quote_mode='always') # KEY1='hello world' +set_key('.env', 'KEY2', value, quote_mode='auto') # KEY2='hello world' (has space) +set_key('.env', 'KEY3', value, quote_mode='never') # KEY3=hello world (may cause issues) + +# Auto mode examples +set_key('.env', 'SIMPLE', 'abc123', quote_mode='auto') # SIMPLE=abc123 (no quotes) +set_key('.env', 'COMPLEX', 'abc-123', quote_mode='auto') # COMPLEX='abc-123' (has hyphen) +``` + +## Export Mode + +When `export=True` is used with `set_key()`, the resulting line is prefixed with `export `, making the .env file compatible with bash sourcing: + +```python +from dotenv import set_key + +set_key('.env', 'PATH_ADDITION', '/usr/local/bin', export=True) +# Results in: export PATH_ADDITION='/usr/local/bin' + +# Now the .env file can be sourced in bash: +# $ source .env +# $ echo $PATH_ADDITION +``` + +## Error Handling + +The file manipulation functions handle various error conditions: + +- **File doesn't exist**: + - `get_key()`: Returns `None` and logs warning with verbose=True + - `unset_key()`: Returns `(None, key)` and logs warning + - `set_key()`: Raises `IOError` if the file path doesn't exist +- **Key doesn't exist**: + - `get_key()`: Returns `None` and logs warning with verbose=True + - `unset_key()`: Returns `(None, key)` and logs warning +- **Invalid quote mode**: `set_key()` raises `ValueError` for modes other than "always", "auto", "never" +- **File permissions**: OS-level `PermissionError` and `IOError` exceptions are propagated to caller +- **Invalid file encoding**: `UnicodeDecodeError` may be raised if encoding parameter doesn't match file + +Examples of error handling: + +```python +from dotenv import get_key, set_key, unset_key + +# Handle missing files +try: + value = get_key('nonexistent.env', 'KEY') + if value is None: + print("Key not found or file doesn't exist") +except IOError as e: + print(f"File access error: {e}") + +# Handle invalid quote modes +try: + set_key('.env', 'KEY', 'value', quote_mode='invalid') +except ValueError as e: + print(f"Invalid quote mode: {e}") + +# Handle file creation failure +try: + set_key('/readonly/path/.env', 'KEY', 'value') +except (IOError, PermissionError) as e: + print(f"Cannot write to file: {e}") +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/index.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/index.md new file mode 100644 index 000000000..2e16161b1 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/docs/index.md @@ -0,0 +1,213 @@ +# python-dotenv + +A Python library that reads key-value pairs from a `.env` file and can set them as environment variables. It helps in the development of applications following the 12-factor principles by providing a convenient way to manage configuration through environment variables. + +## Package Information + +- **Package Name**: python-dotenv +- **Language**: Python +- **Installation**: `pip install python-dotenv` +- **CLI Installation**: `pip install "python-dotenv[cli]"` + +## Core Imports + +```python +from dotenv import load_dotenv, dotenv_values +``` + +Complete imports for all functionality: + +```python +from dotenv import ( + load_dotenv, + dotenv_values, + find_dotenv, + get_key, + set_key, + unset_key, + get_cli_string, + load_ipython_extension +) +``` + +## Basic Usage + +```python +from dotenv import load_dotenv +import os + +# Load environment variables from .env file +load_dotenv() + +# Now you can access environment variables +database_url = os.getenv('DATABASE_URL') +debug_mode = os.getenv('DEBUG', 'False').lower() == 'true' + +# Use environment variables in your application +print(f"Database URL: {database_url}") +print(f"Debug mode: {debug_mode}") +``` + +Alternative approach without modifying environment: + +```python +from dotenv import dotenv_values + +# Parse .env file and return as dictionary +config = dotenv_values(".env") +database_url = config.get('DATABASE_URL') +debug_mode = config.get('DEBUG', 'False').lower() == 'true' +``` + +## Architecture + +python-dotenv follows a modular architecture: + +- **Loading Functions**: `load_dotenv()` and `dotenv_values()` provide the primary interfaces for loading configuration +- **File Discovery**: `find_dotenv()` automatically discovers .env files in the directory hierarchy +- **File Manipulation**: `get_key()`, `set_key()`, `unset_key()` provide programmatic access to .env file contents +- **Variable Expansion**: Built-in support for POSIX-style variable expansion using `${VAR}` syntax +- **CLI Interface**: Complete command-line tool for .env file manipulation +- **IPython Integration**: Magic commands for interactive development environments + +## Capabilities + +### Environment Loading + +Load environment variables from .env files with automatic file discovery, variable expansion, and flexible override options. + +```python { .api } +def load_dotenv( + dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + override: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8" +) -> bool: ... + +def dotenv_values( + dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8" +) -> Dict[str, Optional[str]]: ... + +def find_dotenv( + filename: str = ".env", + raise_error_if_not_found: bool = False, + usecwd: bool = False +) -> str: ... +``` + +[Environment Loading](./environment-loading.md) + +### File Manipulation + +Programmatically read, write, and modify .env files with support for different quote modes and export formats. + +```python { .api } +def get_key( + dotenv_path: Union[str, os.PathLike[str]], + key_to_get: str, + encoding: Optional[str] = "utf-8" +) -> Optional[str]: ... + +def set_key( + dotenv_path: Union[str, os.PathLike[str]], + key_to_set: str, + value_to_set: str, + quote_mode: str = "always", + export: bool = False, + encoding: Optional[str] = "utf-8" +) -> Tuple[Optional[bool], str, str]: ... + +def unset_key( + dotenv_path: Union[str, os.PathLike[str]], + key_to_unset: str, + quote_mode: str = "always", + encoding: Optional[str] = "utf-8" +) -> Tuple[Optional[bool], str]: ... +``` + +[File Manipulation](./file-manipulation.md) + +### Command Line Interface + +Complete CLI tool for managing .env files with list, get, set, unset, and run commands supporting multiple output formats. + +```bash { .api } +dotenv list [--format=FORMAT] +dotenv get KEY +dotenv set KEY VALUE +dotenv unset KEY +dotenv run [--override/--no-override] COMMAND +``` + +[Command Line Interface](./cli.md) + +### IPython Integration + +Magic commands for loading .env files in IPython and Jupyter notebook environments with override and verbose options. + +```python { .api } +def load_ipython_extension(ipython: Any) -> None: ... + +# Magic command usage: +# %load_ext dotenv +# %dotenv [-o] [-v] [dotenv_path] +``` + +[IPython Integration](./ipython.md) + +### Utility Functions + +Helper functions for generating CLI commands and working with dotenv programmatically. + +```python { .api } +def get_cli_string( + path: Optional[str] = None, + action: Optional[str] = None, + key: Optional[str] = None, + value: Optional[str] = None, + quote: Optional[str] = None +) -> str: ... +``` + +[Utilities](./utilities.md) + +## Types + +```python { .api } +# Type aliases +StrPath = Union[str, os.PathLike[str]] + +# Core classes (internal, accessed via functions) +class DotEnv: + def __init__( + self, + dotenv_path: Optional[StrPath], + stream: Optional[IO[str]] = None, + verbose: bool = False, + encoding: Optional[str] = None, + interpolate: bool = True, + override: bool = True + ) -> None: ... + + def dict(self) -> Dict[str, Optional[str]]: ... + def parse(self) -> Iterator[Tuple[str, Optional[str]]]: ... + def set_as_environment_variables(self) -> bool: ... + def get(self, key: str) -> Optional[str]: ... + +# Parser types (internal implementation) +class Binding(NamedTuple): + key: Optional[str] + value: Optional[str] + original: "Original" + error: bool + +class Original(NamedTuple): + string: str + line: int +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md new file mode 100644 index 000000000..b4135137e --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md @@ -0,0 +1,257 @@ +# IPython Integration + +Magic commands for loading .env files in IPython and Jupyter notebook environments with override and verbose options. + +## Installation + +The IPython extension is included with the base python-dotenv package: + +```bash +pip install python-dotenv +``` + +## Capabilities + +### Loading the Extension + +Load the dotenv extension to enable the `%dotenv` magic command. + +```python { .api } +def load_ipython_extension(ipython: Any) -> None: + """ + Register the %dotenv magic command. + + This function is called automatically when the extension is loaded. + """ +``` + +Usage in IPython/Jupyter: + +```python +# Load the extension +%load_ext dotenv + +# Extension is now available for the current session +``` + +### Magic Command + +Use the `%dotenv` magic command to load environment variables from .env files. + +```python { .api } +%dotenv [OPTIONS] [dotenv_path] + +Options: + -o, --override Indicate to override existing variables + -v, --verbose Indicate function calls to be verbose + +Arguments: + dotenv_path Search in increasingly higher folders for the dotenv_path (default: '.env') +``` + +## Usage Examples + +### Basic Usage + +```python +# Load extension +%load_ext dotenv + +# Load from default .env file +%dotenv + +# Now environment variables from .env are available +import os +database_url = os.getenv('DATABASE_URL') +print(f"Database URL: {database_url}") +``` + +### Loading from Specific Files + +```python +# Load from specific file in current directory +%dotenv config.env + +# Load from relative path +%dotenv ../shared.env + +# Load from absolute path +%dotenv /path/to/production.env +``` + +### Using Options + +```python +# Load with verbose output +%dotenv -v +# Output: Loading .env file found at: /current/directory/.env + +# Override existing environment variables +%dotenv -o + +# Combine options +%dotenv -o -v production.env +``` + +### Multiple Environment Files + +```python +# Load multiple files in sequence +%dotenv shared.env +%dotenv -o local.env # Override with local settings + +# Load different files for different purposes +%dotenv database.env # Database configuration +%dotenv api.env # API keys and endpoints +%dotenv -o debug.env # Debug settings (override existing) +``` + +## Integration with Jupyter Notebooks + +The IPython integration is particularly useful in Jupyter notebooks for data science and development workflows: + +```python +# Cell 1: Load environment configuration +%load_ext dotenv +%dotenv -v config.env + +# Cell 2: Use environment variables +import os +import pandas as pd +from sqlalchemy import create_engine + +# Get database connection from environment +database_url = os.getenv('DATABASE_URL') +engine = create_engine(database_url) + +# Load data +df = pd.read_sql('SELECT * FROM users', engine) +df.head() +``` + +```python +# Example: Different environments for different notebook sections +# Development section +%dotenv -v development.env + +# Load development data +dev_api_key = os.getenv('API_KEY') +dev_data = fetch_data(dev_api_key) + +# Production comparison section +%dotenv -o production.env + +# Load production data +prod_api_key = os.getenv('API_KEY') +prod_data = fetch_data(prod_api_key) + +# Compare datasets +compare_data(dev_data, prod_data) +``` + +## Options Details + +### Override Option (`-o, --override`) + +Controls whether environment variables from the .env file override existing environment variables: + +```python +# Without override (default behavior) +import os +os.environ['DEBUG'] = 'False' # Set in current environment +%dotenv # .env contains DEBUG=True +print(os.getenv('DEBUG')) # Prints: False (existing value preserved) + +# With override +import os +os.environ['DEBUG'] = 'False' # Set in current environment +%dotenv -o # .env contains DEBUG=True +print(os.getenv('DEBUG')) # Prints: True (overridden by .env) +``` + +### Verbose Option (`-v, --verbose`) + +Enables verbose output to show which .env file is being loaded: + +```python +# Without verbose +%dotenv config.env +# (no output) + +# With verbose +%dotenv -v config.env +# Output: Loading .env file found at: /current/directory/config.env +``` + +## Error Handling + +The magic command handles common error scenarios gracefully: + +```python +# File not found +%dotenv nonexistent.env +# Output: cannot find .env file + +# No file specified and no default .env +%dotenv +# (silently continues if no .env file found) + +# With verbose, shows search behavior +%dotenv -v +# Output: Loading .env file found at: /path/to/.env +# Or: python-dotenv could not find configuration file .env +``` + +## Automatic Extension Loading + +For convenience, you can automatically load the extension when starting IPython by adding it to your IPython configuration: + +```python +# In ~/.ipython/profile_default/ipython_config.py +c.InteractiveShellApp.extensions = ['dotenv'] + +# Or add to startup file +# In ~/.ipython/profile_default/startup/00-dotenv.py +get_ipython().magic('load_ext dotenv') +``` + +## Working with Virtual Environments + +The IPython integration works seamlessly with virtual environments: + +```python +# In a virtual environment +%load_ext dotenv +%dotenv -v + +# Variables are loaded for the current Python session +import os +print(f"Virtual env: {os.getenv('VIRTUAL_ENV')}") +print(f"App config: {os.getenv('APP_CONFIG')}") +``` + +## Best Practices + +1. **Load early**: Load your .env files early in your notebook before importing other modules +2. **Use verbose mode**: Use `-v` for debugging to see which files are being loaded +3. **Organize by purpose**: Use separate .env files for different configurations (database, API keys, debug settings) +4. **Override carefully**: Use `-o` only when you need to override existing environment variables +5. **Document in notebooks**: Add markdown cells explaining which .env files are needed + +```python +# Good practice example +%load_ext dotenv + +# Load base configuration +%dotenv -v base.env + +# Load environment-specific overrides +%dotenv -o -v development.env + +# Now proceed with analysis +import pandas as pd +import requests + +api_key = os.getenv('API_KEY') +database_url = os.getenv('DATABASE_URL') +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md new file mode 100644 index 000000000..7eb4e9b68 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md @@ -0,0 +1,276 @@ +# Utilities + +Helper functions for generating CLI commands and working with dotenv programmatically. + +## Capabilities + +### CLI Command Generation + +Generate shell command strings for dotenv operations, useful for automation and integration with other tools. + +```python { .api } +def get_cli_string( + path: Optional[str] = None, + action: Optional[str] = None, + key: Optional[str] = None, + value: Optional[str] = None, + quote: Optional[str] = None +) -> str: + """ + Return a string suitable for running as a shell script. + + Useful for converting arguments passed to a fabric task + to be passed to a `local` or `run` command. + + Parameters: + path: Path to the .env file (-f option) + action: CLI action (get, set, unset, list, run) + key: Key name for get/set/unset operations + value: Value for set operations + quote: Quote mode (-q option: always, auto, never) + + Returns: + str: Complete dotenv command string ready for shell execution + """ +``` + +## Usage Examples + +### Basic Command Generation + +```python +from dotenv import get_cli_string + +# Generate basic commands +cmd = get_cli_string(action='list') +print(cmd) # Output: dotenv list + +cmd = get_cli_string(action='get', key='DATABASE_URL') +print(cmd) # Output: dotenv get DATABASE_URL + +cmd = get_cli_string(action='set', key='DEBUG', value='True') +print(cmd) # Output: dotenv set DEBUG True +``` + +### Commands with File Paths + +```python +# Generate commands with custom file paths +cmd = get_cli_string(path='production.env', action='list') +print(cmd) # Output: dotenv -f production.env list + +cmd = get_cli_string(path='/etc/app/.env', action='get', key='API_KEY') +print(cmd) # Output: dotenv -f /etc/app/.env get API_KEY + +cmd = get_cli_string(path='config.env', action='set', key='HOST', value='localhost') +print(cmd) # Output: dotenv -f config.env set HOST localhost +``` + +### Commands with Quote Modes + +```python +# Generate commands with different quote modes +cmd = get_cli_string(action='set', key='SIMPLE', value='abc', quote='never') +print(cmd) # Output: dotenv -q never set SIMPLE abc + +cmd = get_cli_string(action='set', key='COMPLEX', value='hello world', quote='auto') +print(cmd) # Output: dotenv -q auto set COMPLEX "hello world" + +cmd = get_cli_string(action='set', key='QUOTED', value='always', quote='always') +print(cmd) # Output: dotenv -q always set QUOTED always +``` + +### Complex Command Generation + +```python +# Generate complex commands with multiple options +cmd = get_cli_string( + path='staging.env', + action='set', + key='DATABASE_URL', + value='postgresql://user:pass@localhost/db', + quote='always' +) +print(cmd) # Output: dotenv -q always -f staging.env set DATABASE_URL "postgresql://user:pass@localhost/db" +``` + +### Value Handling + +The function automatically handles values that contain spaces by wrapping them in quotes: + +```python +# Values with spaces are automatically quoted +cmd = get_cli_string(action='set', key='MESSAGE', value='hello world') +print(cmd) # Output: dotenv set MESSAGE "hello world" + +# Values without spaces remain unquoted +cmd = get_cli_string(action='set', key='PORT', value='8080') +print(cmd) # Output: dotenv set PORT 8080 + +# Empty or None values are handled gracefully +cmd = get_cli_string(action='get', key='MISSING_KEY') +print(cmd) # Output: dotenv get MISSING_KEY +``` + +## Integration Examples + +### Fabric Integration + +```python +from fabric import task +from dotenv import get_cli_string + +@task +def deploy_config(c, env='production'): + """Deploy configuration to remote server.""" + + # Generate commands for different environments + commands = [ + get_cli_string(path=f'{env}.env', action='set', key='DEPLOYED_AT', value='$(date)'), + get_cli_string(path=f'{env}.env', action='set', key='VERSION', value='1.2.3'), + get_cli_string(path=f'{env}.env', action='list', quote='export') + ] + + for cmd in commands: + c.run(cmd) +``` + +### Automation Scripts + +```python +import subprocess +from dotenv import get_cli_string + +def setup_environment(config_file, settings): + """Automate environment setup using generated commands.""" + + commands = [] + + # Generate set commands for all settings + for key, value in settings.items(): + cmd = get_cli_string( + path=config_file, + action='set', + key=key, + value=str(value), + quote='auto' + ) + commands.append(cmd) + + # Execute all commands + for cmd in commands: + print(f"Executing: {cmd}") + subprocess.run(cmd, shell=True, check=True) + + # List final configuration + list_cmd = get_cli_string(path=config_file, action='list') + subprocess.run(list_cmd, shell=True) + +# Usage +settings = { + 'DATABASE_URL': 'postgresql://localhost/myapp', + 'REDIS_URL': 'redis://localhost:6379', + 'DEBUG': 'False', + 'SECRET_KEY': 'production-secret-key' +} + +setup_environment('production.env', settings) +``` + +### Build System Integration + +```python +import os +from dotenv import get_cli_string + +def generate_build_commands(environment='development'): + """Generate build commands with environment-specific configuration.""" + + env_file = f'.env.{environment}' + + # Commands for build process + commands = [ + # Set build-specific variables + get_cli_string(path=env_file, action='set', key='NODE_ENV', value=environment), + get_cli_string(path=env_file, action='set', key='BUILD_TIME', value='$(date)'), + + # Run build with environment + get_cli_string(path=env_file, action='run') + ' npm run build', + get_cli_string(path=env_file, action='run') + ' npm run test', + ] + + return commands + +# Generate commands for different environments +dev_commands = generate_build_commands('development') +prod_commands = generate_build_commands('production') + +print("Development build commands:") +for cmd in dev_commands: + print(f" {cmd}") + +print("\nProduction build commands:") +for cmd in prod_commands: + print(f" {cmd}") +``` + +### Testing Integration + +```python +import pytest +from dotenv import get_cli_string +import subprocess + +class TestEnvironmentCommands: + """Test environment command generation.""" + + def test_basic_commands(self): + """Test basic command generation.""" + assert get_cli_string(action='list') == 'dotenv list' + assert get_cli_string(action='get', key='TEST') == 'dotenv get TEST' + + def test_file_path_commands(self): + """Test commands with file paths.""" + cmd = get_cli_string(path='test.env', action='list') + assert cmd == 'dotenv -f test.env list' + + def test_quote_modes(self): + """Test different quote modes.""" + cmd = get_cli_string(action='set', key='KEY', value='value', quote='never') + assert 'never' in cmd + + def test_command_execution(self, tmp_path): + """Test actual command execution.""" + env_file = tmp_path / '.env' + + # Generate and execute set command + set_cmd = get_cli_string( + path=str(env_file), + action='set', + key='TEST_KEY', + value='test_value' + ) + + subprocess.run(set_cmd, shell=True, check=True) + + # Verify the key was set + get_cmd = get_cli_string(path=str(env_file), action='get', key='TEST_KEY') + result = subprocess.run(get_cmd, shell=True, capture_output=True, text=True) + + assert result.stdout.strip() == 'test_value' +``` + +## Command String Format + +The generated command strings follow this format: + +```bash +dotenv [global_options] action [action_arguments] +``` + +Where: +- **global_options**: `-q quote_mode`, `-f file_path` +- **action**: `list`, `get`, `set`, `unset`, `run` +- **action_arguments**: Depend on the specific action + +The function ensures proper ordering and quoting of arguments to generate valid shell commands. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/tile.json b/.tessl/tiles/tessl/pypi-python-dotenv/tile.json new file mode 100644 index 000000000..7285a3a59 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-python-dotenv/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-python-dotenv", + "version": "1.1.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/python-dotenv@1.1.1", + "summary": "Read key-value pairs from a .env file and set them as environment variables" +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/adapters.md b/.tessl/tiles/tessl/pypi-requests/docs/adapters.md new file mode 100644 index 000000000..500785a7a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/adapters.md @@ -0,0 +1,571 @@ +# Transport Adapters + +Transport adapters handle the actual HTTP communication, providing the interface between requests and underlying HTTP libraries. Adapters enable connection pooling, SSL/TLS handling, and protocol-specific optimizations. + +## Capabilities + +### BaseAdapter Class + +Abstract base class that all transport adapters inherit from. + +```python { .api } +class BaseAdapter: + """ + The base adapter class for transports. + + All transport adapters should inherit from this class. + """ + + def __init__(self): + """Initialize the adapter.""" + + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None) -> 'Response': + """ + Send a PreparedRequest and return a Response. + + Parameters: + - request: PreparedRequest object to send + - stream: Whether to stream the response content + - timeout: Timeout value in seconds + - verify: SSL verification setting + - cert: Client certificate + - proxies: Proxy configuration + + Returns: + Response object + + Raises: + NotImplementedError: Must be implemented by subclasses + """ + + def close(self): + """ + Clean up adapter resources. + + Called when session is closed. + """ +``` + +### HTTPAdapter Class + +Built-in HTTP/HTTPS adapter using urllib3 for connection pooling and transport. + +```python { .api } +class HTTPAdapter(BaseAdapter): + """ + Built-in HTTP adapter using urllib3. + + Provides connection pooling, SSL/TLS handling, and HTTP protocol support. + """ + + def __init__(self, pool_connections=10, pool_maxsize=10, max_retries=0, + pool_block=False): + """ + Initialize HTTPAdapter. + + Parameters: + - pool_connections: Number of urllib3 connection pools to cache + - pool_maxsize: Maximum connections per pool + - max_retries: Maximum number of retries per request + - pool_block: Whether to block when pool is full + """ + + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None) -> 'Response': + """ + Send a request using urllib3. + + Parameters: + - request: PreparedRequest to send + - stream: Stream response content + - timeout: Request timeout (connect, read) tuple or single value + - verify: SSL certificate verification (bool or CA bundle path) + - cert: Client certificate (path or (cert, key) tuple) + - proxies: Proxy configuration dict + + Returns: + Response object + """ + + def close(self): + """Close all pooled connections.""" + + # Connection management + def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): + """ + Initialize urllib3 PoolManager. + + Parameters: + - connections: Number of connection pools + - maxsize: Maximum connections per pool + - block: Whether to block when pool is full + - **pool_kwargs: Additional pool arguments + """ + + def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): + """ + Get connection pool with TLS context. + + Parameters: + - request: Request object + - verify: SSL verification setting + - proxies: Proxy configuration + - cert: Client certificate + + Returns: + ConnectionPool instance + """ + + def get_connection(self, url, proxies=None): + """ + DEPRECATED: Get connection for URL. + + Parameters: + - url: URL to get connection for + - proxies: Proxy configuration + + Returns: + ConnectionPool instance + """ + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """ + Get ProxyManager for proxy URL. + + Parameters: + - proxy: Proxy URL + - **proxy_kwargs: Additional proxy arguments + + Returns: + ProxyManager instance + """ + + # Request/Response processing + def build_response(self, req, resp) -> 'Response': + """ + Build Response object from urllib3 response. + + Parameters: + - req: PreparedRequest object + - resp: urllib3 HTTPResponse object + + Returns: + Response object + """ + + def request_url(self, request, proxies) -> str: + """ + Get the URL to use for the request. + + Parameters: + - request: PreparedRequest object + - proxies: Proxy configuration + + Returns: + URL string to use + """ + + def add_headers(self, request, **kwargs): + """ + Add headers to the request. + + Parameters: + - request: PreparedRequest object + - **kwargs: Additional arguments + """ + + def proxy_headers(self, proxy) -> dict: + """ + Get headers to add for proxy requests. + + Parameters: + - proxy: Proxy URL + + Returns: + Dict of headers + """ + + # SSL/TLS handling + def cert_verify(self, conn, url, verify, cert): + """ + Verify SSL certificates and configure client certs. + + Parameters: + - conn: Connection object + - url: Request URL + - verify: SSL verification setting + - cert: Client certificate + """ + + def build_connection_pool_key_attributes(self, request, verify, cert=None) -> tuple: + """ + Build key attributes for connection pooling. + + Parameters: + - request: PreparedRequest object + - verify: SSL verification setting + - cert: Client certificate + + Returns: + Tuple of (pool_kwargs, connection_pool_kwargs) + """ +``` + +### Adapter Constants + +```python { .api } +DEFAULT_POOLBLOCK: bool # False +DEFAULT_POOLSIZE: int # 10 +DEFAULT_RETRIES: int # 0 +DEFAULT_POOL_TIMEOUT: None # None +``` + +## Usage Examples + +### Basic Adapter Usage + +```python +import requests +from requests.adapters import HTTPAdapter + +# Adapters are used automatically +response = requests.get('https://httpbin.org/get') +print(f"Status: {response.status_code}") + +# Access session adapters +session = requests.Session() +print("Mounted adapters:") +for prefix, adapter in session.adapters.items(): + print(f" {prefix}: {adapter}") +``` + +### Custom Adapter Configuration + +```python +import requests +from requests.adapters import HTTPAdapter + +# Create adapter with custom settings +adapter = HTTPAdapter( + pool_connections=20, # More connection pools + pool_maxsize=50, # More connections per pool + max_retries=3, # Retry failed requests + pool_block=True # Wait when pool is full +) + +# Mount adapter to session +session = requests.Session() +session.mount('https://', adapter) +session.mount('http://', adapter) + +# Requests will use the custom adapter +response = session.get('https://httpbin.org/get') +``` + +### Protocol-Specific Adapters + +```python +import requests +from requests.adapters import HTTPAdapter + +# Different adapters for different hosts +class APIAdapter(HTTPAdapter): + """Custom adapter for API endpoints.""" + + def __init__(self, api_key, **kwargs): + self.api_key = api_key + super().__init__(**kwargs) + + def add_headers(self, request, **kwargs): + request.headers['Authorization'] = f'Bearer {self.api_key}' + super().add_headers(request, **kwargs) + +# Mount custom adapter +session = requests.Session() +api_adapter = APIAdapter(api_key='your-api-key', max_retries=3) +session.mount('https://api.example.com/', api_adapter) + +# Regular adapter for other URLs +session.mount('https://', HTTPAdapter(max_retries=1)) + +# Different adapters used based on URL +api_response = session.get('https://api.example.com/data') # Uses APIAdapter +web_response = session.get('https://other-site.com/page') # Uses HTTPAdapter +``` + +### Connection Pool Management + +```python +import requests +from requests.adapters import HTTPAdapter + +# Configure connection pooling +adapter = HTTPAdapter( + pool_connections=10, # 10 connection pools + pool_maxsize=100, # 100 connections per pool + pool_block=False # Don't block when pool full +) + +session = requests.Session() +session.mount('https://', adapter) + +# Make many requests - connections are pooled and reused +urls = [f'https://httpbin.org/delay/{i}' for i in range(5)] + +for url in urls: + response = session.get(url) + print(f"Response from {url}: {response.status_code}") + +# Close session to clean up connection pools +session.close() +``` + +### SSL/TLS Configuration + +```python +import requests +from requests.adapters import HTTPAdapter + +# Custom adapter with SSL settings +class SecureAdapter(HTTPAdapter): + def cert_verify(self, conn, url, verify, cert): + # Custom SSL verification logic + super().cert_verify(conn, url, verify, cert) + print(f"SSL verification for {url}: verify={verify}") + +# Use secure adapter +session = requests.Session() +session.mount('https://', SecureAdapter()) + +# Configure SSL verification +response = session.get('https://httpbin.org/get', + verify=True, # Verify SSL certificates + cert=None) # No client certificate +``` + +### Retry Configuration + +```python +import requests +from requests.adapters import HTTPAdapter +from urllib3.util.retry import Retry + +# Configure retry strategy +retry_strategy = Retry( + total=3, # Total retries + status_forcelist=[429, 500, 502, 503, 504], # Status codes to retry + method_whitelist=["HEAD", "GET", "OPTIONS"], # Methods to retry + backoff_factor=1 # Backoff between retries +) + +# Create adapter with retry strategy +adapter = HTTPAdapter(max_retries=retry_strategy) + +session = requests.Session() +session.mount('http://', adapter) +session.mount('https://', adapter) + +# Requests will automatically retry on failures +response = session.get('https://httpbin.org/status/500') +``` + +### Custom Transport Adapter + +```python +import requests +from requests.adapters import BaseAdapter +from requests.models import Response + +class MockAdapter(BaseAdapter): + """Mock adapter for testing.""" + + def __init__(self, responses=None): + super().__init__() + self.responses = responses or {} + + def send(self, request, **kwargs): + """Return mock response.""" + response = Response() + response.status_code = 200 + response.headers['Content-Type'] = 'application/json' + + # Mock response based on URL + if request.url in self.responses: + response._content = self.responses[request.url].encode('utf-8') + else: + response._content = b'{"mock": "response"}' + + response.url = request.url + response.request = request + return response + + def close(self): + pass + +# Use mock adapter for testing +mock_responses = { + 'https://api.example.com/data': '{"users": [{"name": "John"}]}' +} + +session = requests.Session() +session.mount('https://api.example.com/', MockAdapter(mock_responses)) + +# This returns the mock response +response = session.get('https://api.example.com/data') +print(response.json()) # {'users': [{'name': 'John'}]} +``` + +### Proxy Adapter Configuration + +```python +import requests +from requests.adapters import HTTPAdapter + +class ProxyAdapter(HTTPAdapter): + """Adapter with proxy configuration.""" + + def __init__(self, proxy_url, **kwargs): + self.proxy_url = proxy_url + super().__init__(**kwargs) + + def proxy_headers(self, proxy): + """Add custom proxy headers.""" + headers = super().proxy_headers(proxy) + headers['Proxy-Authorization'] = 'Basic dXNlcjpwYXNz' # base64 user:pass + return headers + +# Configure proxy adapter +proxy_adapter = ProxyAdapter('http://proxy.example.com:8080') + +session = requests.Session() +session.mount('http://', proxy_adapter) +session.mount('https://', proxy_adapter) + +# Requests go through the proxy +response = session.get('https://httpbin.org/ip') +print(response.json()) # Shows proxy IP +``` + +### Performance Monitoring Adapter + +```python +import requests +import time +from requests.adapters import HTTPAdapter + +class TimingAdapter(HTTPAdapter): + """Adapter that measures request timing.""" + + def send(self, request, **kwargs): + start_time = time.time() + response = super().send(request, **kwargs) + end_time = time.time() + + # Add timing information to response + response.elapsed_total = end_time - start_time + print(f"Request to {request.url} took {response.elapsed_total:.3f}s") + + return response + +# Use timing adapter +session = requests.Session() +session.mount('https://', TimingAdapter()) + +response = session.get('https://httpbin.org/delay/2') +print(f"Total time: {response.elapsed_total:.3f}s") +``` + +### Adapter Debugging + +```python +import requests +from requests.adapters import HTTPAdapter + +class DebugAdapter(HTTPAdapter): + """Adapter with detailed logging.""" + + def send(self, request, **kwargs): + print(f"Sending {request.method} request to {request.url}") + print(f"Headers: {dict(request.headers)}") + + if request.body: + print(f"Body: {request.body[:100]}...") + + response = super().send(request, **kwargs) + + print(f"Received {response.status_code} response") + print(f"Response headers: {dict(response.headers)}") + + return response + +# Use debug adapter +session = requests.Session() +session.mount('https://', DebugAdapter()) + +response = session.post('https://httpbin.org/post', + json={'key': 'value'}) +``` + +## Adapter Best Practices + +### Resource Management + +```python +import requests +from requests.adapters import HTTPAdapter + +# Always close sessions to clean up connection pools +session = requests.Session() +adapter = HTTPAdapter(pool_connections=10, pool_maxsize=50) +session.mount('https://', adapter) + +try: + # Use session + response = session.get('https://api.example.com/data') + # Process response... +finally: + # Ensure cleanup + session.close() + +# Or use context manager +with requests.Session() as session: + adapter = HTTPAdapter(pool_connections=10, pool_maxsize=50) + session.mount('https://', adapter) + response = session.get('https://api.example.com/data') +# Session automatically closed +``` + +### Adapter Selection + +```python +import requests +from requests.adapters import HTTPAdapter + +# Different configurations for different services +session = requests.Session() + +# High-performance adapter for API calls +api_adapter = HTTPAdapter( + pool_connections=20, + pool_maxsize=100, + max_retries=3 +) + +# Conservative adapter for file downloads +download_adapter = HTTPAdapter( + pool_connections=5, + pool_maxsize=10, + max_retries=1 +) + +# Mount adapters with specific prefixes +session.mount('https://api.fastservice.com/', api_adapter) +session.mount('https://downloads.example.com/', download_adapter) +session.mount('https://', HTTPAdapter()) # Default adapter + +# Requests automatically use appropriate adapter +api_response = session.get('https://api.fastservice.com/data') +file_response = session.get('https://downloads.example.com/file.zip') +other_response = session.get('https://other-site.com/page') +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/authentication.md b/.tessl/tiles/tessl/pypi-requests/docs/authentication.md new file mode 100644 index 000000000..b274af7ca --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/authentication.md @@ -0,0 +1,344 @@ +# Authentication + +Authentication handlers for various HTTP authentication schemes. The requests library provides built-in support for the most common authentication methods and allows for custom authentication handlers. + +## Capabilities + +### Base Authentication Class + +All authentication handlers inherit from the AuthBase class. + +```python { .api } +class AuthBase: + """Base class that all authentication implementations derive from.""" + + def __call__(self, request): + """ + Apply authentication to a request. + + Parameters: + - request: PreparedRequest object to modify + + Returns: + Modified PreparedRequest object + """ +``` + +### HTTP Basic Authentication + +Implements HTTP Basic Authentication using username and password. + +```python { .api } +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + def __init__(self, username: str, password: str): + """ + Initialize Basic Authentication. + + Parameters: + - username: Username for authentication + - password: Password for authentication + """ + + def __call__(self, request) -> 'PreparedRequest': + """Apply Basic Authentication to request.""" + + def __eq__(self, other) -> bool: + """Check equality with another auth object.""" + + def __ne__(self, other) -> bool: + """Check inequality with another auth object.""" +``` + +### HTTP Digest Authentication + +Implements HTTP Digest Authentication for enhanced security over Basic auth. + +```python { .api } +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + def __init__(self, username: str, password: str): + """ + Initialize Digest Authentication. + + Parameters: + - username: Username for authentication + - password: Password for authentication + """ + + def __call__(self, request) -> 'PreparedRequest': + """Apply Digest Authentication to request.""" +``` + +### HTTP Proxy Authentication + +Implements HTTP Proxy Authentication, extending Basic Authentication for proxy servers. + +```python { .api } +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, request) -> 'PreparedRequest': + """Apply Proxy Authentication to request.""" +``` + +## Usage Examples + +### Basic Authentication + +```python +import requests +from requests.auth import HTTPBasicAuth + +# Using auth tuple (shorthand) +response = requests.get('https://api.example.com/data', auth=('username', 'password')) + +# Using HTTPBasicAuth class +basic_auth = HTTPBasicAuth('username', 'password') +response = requests.get('https://api.example.com/data', auth=basic_auth) + +# With sessions for persistent auth +session = requests.Session() +session.auth = ('username', 'password') +response = session.get('https://api.example.com/data') +``` + +### Digest Authentication + +```python +import requests +from requests.auth import HTTPDigestAuth + +# Digest authentication for enhanced security +digest_auth = HTTPDigestAuth('username', 'password') +response = requests.get('https://api.example.com/secure', auth=digest_auth) + +# With sessions +session = requests.Session() +session.auth = HTTPDigestAuth('user', 'pass') +response = session.get('https://api.example.com/data') +``` + +### Proxy Authentication + +```python +import requests +from requests.auth import HTTPProxyAuth + +# Authenticate with proxy server +proxy_auth = HTTPProxyAuth('proxy_user', 'proxy_pass') +proxies = {'http': 'http://proxy.example.com:8080'} + +response = requests.get('https://api.example.com/data', + auth=proxy_auth, + proxies=proxies) +``` + +### Custom Authentication + +```python +import requests +from requests.auth import AuthBase + +class TokenAuth(AuthBase): + """Custom token-based authentication.""" + + def __init__(self, token): + self.token = token + + def __call__(self, request): + request.headers['Authorization'] = f'Bearer {self.token}' + return request + +# Use custom authentication +token_auth = TokenAuth('your-api-token') +response = requests.get('https://api.example.com/data', auth=token_auth) +``` + +### OAuth 1.0 Authentication + +```python +# Note: OAuth requires additional libraries like requests-oauthlib +from requests_oauthlib import OAuth1 + +# OAuth 1.0 authentication +oauth = OAuth1('client_key', 'client_secret', 'resource_owner_key', 'resource_owner_secret') +response = requests.get('https://api.twitter.com/1.1/account/verify_credentials.json', auth=oauth) +``` + +### Bearer Token Authentication + +```python +import requests + +# Simple bearer token authentication +headers = {'Authorization': 'Bearer your-access-token'} +response = requests.get('https://api.example.com/data', headers=headers) + +# Or create a custom auth class +class BearerAuth(requests.auth.AuthBase): + def __init__(self, token): + self.token = token + + def __call__(self, r): + r.headers["Authorization"] = f"Bearer {self.token}" + return r + +# Use the custom auth +bearer_auth = BearerAuth('your-access-token') +response = requests.get('https://api.example.com/data', auth=bearer_auth) +``` + +### Multiple Authentication Methods + +```python +import requests +from requests.auth import HTTPBasicAuth, HTTPDigestAuth + +# Try different auth methods +def authenticate_with_fallback(url, username, password): + # Try Digest first (more secure) + try: + response = requests.get(url, auth=HTTPDigestAuth(username, password)) + if response.status_code == 200: + return response + except requests.exceptions.RequestException: + pass + + # Fall back to Basic auth + try: + response = requests.get(url, auth=HTTPBasicAuth(username, password)) + return response + except requests.exceptions.RequestException: + pass + + # Try without auth + return requests.get(url) + +response = authenticate_with_fallback('https://api.example.com/data', 'user', 'pass') +``` + +### Session-based Authentication + +```python +import requests + +# Login to get session cookie +session = requests.Session() +login_data = {'username': 'user', 'password': 'pass'} +session.post('https://example.com/login', data=login_data) + +# Subsequent requests use the session cookie +response = session.get('https://example.com/protected-data') + +# Or combine with other auth methods +session.auth = ('api_user', 'api_pass') +response = session.get('https://api.example.com/data') +``` + +## Authentication with Different Request Types + +### GET with Authentication + +```python +import requests + +# Simple GET with auth +response = requests.get('https://api.example.com/users', auth=('user', 'pass')) +users = response.json() +``` + +### POST with Authentication + +```python +import requests + +# POST data with authentication +data = {'name': 'John Doe', 'email': 'john@example.com'} +response = requests.post('https://api.example.com/users', + json=data, + auth=('admin', 'password')) +``` + +### File Upload with Authentication + +```python +import requests + +# Upload file with authentication +files = {'file': open('document.pdf', 'rb')} +response = requests.post('https://api.example.com/upload', + files=files, + auth=('user', 'pass')) +``` + +## Security Considerations + +### HTTPS Requirement + +Always use HTTPS when sending authentication credentials: + +```python +import requests + +# Good - HTTPS protects credentials +response = requests.get('https://api.example.com/data', auth=('user', 'pass')) + +# Bad - HTTP exposes credentials +# response = requests.get('http://api.example.com/data', auth=('user', 'pass')) +``` + +### Environment Variables + +Store credentials in environment variables instead of hardcoding: + +```python +import os +import requests + +username = os.environ.get('API_USERNAME') +password = os.environ.get('API_PASSWORD') + +if username and password: + response = requests.get('https://api.example.com/data', auth=(username, password)) +else: + raise ValueError("Missing authentication credentials") +``` + +### Certificate Verification + +Always verify SSL certificates in production: + +```python +import requests + +# Default behavior - verify SSL certificates +response = requests.get('https://api.example.com/data', + auth=('user', 'pass'), + verify=True) # This is the default + +# Only disable verification for testing +# response = requests.get('https://api.example.com/data', +# auth=('user', 'pass'), +# verify=False) # Only for testing! +``` + +## Authentication Utility Function + +```python { .api } +def _basic_auth_str(username: str, password: str) -> str: + """ + Generate a Basic Auth string. + + Parameters: + - username: Username for authentication + - password: Password for authentication + + Returns: + Basic authentication header value + """ +``` + +This utility function is used internally by HTTPBasicAuth but can be useful for custom authentication implementations. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/cookies.md b/.tessl/tiles/tessl/pypi-requests/docs/cookies.md new file mode 100644 index 000000000..784f9e30c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/cookies.md @@ -0,0 +1,398 @@ +# Cookie Handling + +Cookie management functionality providing a dict-like interface for handling HTTP cookies with compatibility for both client and server-side cookie operations. + +## Capabilities + +### RequestsCookieJar Class + +A CookieJar with dict-like interface that extends standard cookielib functionality. + +```python { .api } +class RequestsCookieJar: + """ + Compatibility class for http.cookiejar.CookieJar with dict-like interface. + + Provides convenient access to cookies while maintaining full CookieJar functionality. + """ + + def __init__(self, policy=None): + """ + Initialize RequestsCookieJar. + + Parameters: + - policy: CookiePolicy instance (optional) + """ + + # Dict-like interface + def get(self, name: str, default=None, domain=None, path=None) -> str: + """ + Get cookie value by name. + + Parameters: + - name: Cookie name + - default: Default value if cookie not found + - domain: Specific domain to search (optional) + - path: Specific path to search (optional) + + Returns: + Cookie value or default + """ + + def set(self, name: str, value: str, **kwargs): + """ + Set a cookie. + + Parameters: + - name: Cookie name + - value: Cookie value + - **kwargs: Additional cookie attributes (domain, path, etc.) + + Returns: + Cookie object + """ + + def __getitem__(self, name: str) -> str: + """Get cookie value by name.""" + + def __setitem__(self, name: str, value: str): + """Set cookie value by name.""" + + def __delitem__(self, name: str): + """Delete cookie by name.""" + + def __contains__(self, name: str) -> bool: + """Check if cookie exists.""" + + def update(self, other): + """Update cookies from another cookie jar or dict.""" + + def keys(self) -> list[str]: + """Get list of cookie names.""" + + def values(self) -> list[str]: + """Get list of cookie values.""" + + def items(self) -> list[tuple[str, str]]: + """Get list of (name, value) tuples.""" + + def iterkeys(self): + """Iterate over cookie names.""" + + def itervalues(self): + """Iterate over cookie values.""" + + def iteritems(self): + """Iterate over (name, value) tuples.""" + + # Cookie management methods + def list_domains(self) -> list[str]: + """Get list of domains with cookies.""" + + def list_paths(self) -> list[str]: + """Get list of paths with cookies.""" + + def multiple_domains(self) -> bool: + """Check if cookies exist for multiple domains.""" + + def get_dict(self, domain=None, path=None) -> dict: + """ + Get cookies as a plain dict. + + Parameters: + - domain: Filter by domain (optional) + - path: Filter by path (optional) + + Returns: + Dict of cookie name -> value mappings + """ + + def copy(self) -> 'RequestsCookieJar': + """Create a copy of the cookie jar.""" + + def get_policy(self): + """Get the cookie policy.""" +``` + +### Cookie Utility Functions + +Functions for cookie manipulation and conversion. + +```python { .api } +def extract_cookies_to_jar(jar, request, response): + """ + Extract cookies from response and add to jar. + + Parameters: + - jar: RequestsCookieJar to add cookies to + - request: Request object + - response: Response object + """ + +def get_cookie_header(jar, request) -> str | None: + """ + Get Cookie header value from jar for request. + + Parameters: + - jar: RequestsCookieJar containing cookies + - request: Request object + + Returns: + Cookie header value or None + """ + +def remove_cookie_by_name(cookiejar, name: str, domain=None, path=None): + """ + Remove cookie from jar by name. + + Parameters: + - cookiejar: CookieJar to remove from + - name: Cookie name to remove + - domain: Domain filter (optional) + - path: Path filter (optional) + """ + +def create_cookie(name: str, value: str, **kwargs): + """ + Create a Cookie object. + + Parameters: + - name: Cookie name + - value: Cookie value + - **kwargs: Additional cookie attributes + + Returns: + Cookie object + """ + +def morsel_to_cookie(morsel): + """ + Convert http.cookies.Morsel to Cookie object. + + Parameters: + - morsel: Morsel object to convert + + Returns: + Cookie object + """ + +def cookiejar_from_dict(cookie_dict: dict, cookiejar=None, overwrite=True): + """ + Create CookieJar from dictionary. + + Parameters: + - cookie_dict: Dict of cookie name -> value mappings + - cookiejar: Existing jar to add to (optional) + - overwrite: Whether to overwrite existing cookies + + Returns: + CookieJar containing the cookies + """ + +def merge_cookies(cookiejar, cookies): + """ + Merge cookies into an existing jar. + + Parameters: + - cookiejar: Target CookieJar + - cookies: Cookies to merge (dict or CookieJar) + + Returns: + Updated CookieJar + """ +``` + +### Cookie Exceptions + +```python { .api } +class CookieConflictError(RuntimeError): + """Raised when multiple cookies match the same criteria.""" +``` + +## Usage Examples + +### Basic Cookie Operations + +```python +import requests + +# Cookies are automatically handled +response = requests.get('https://httpbin.org/cookies/set/sessionid/abc123') +print(response.cookies['sessionid']) # 'abc123' + +# Access cookie jar +jar = response.cookies +print(jar.get('sessionid')) # 'abc123' +print('sessionid' in jar) # True + +# Send cookies with request +cookies = {'user': 'john', 'token': 'xyz789'} +response = requests.get('https://httpbin.org/cookies', cookies=cookies) +``` + +### Session Cookie Persistence + +```python +import requests + +# Cookies persist across requests in a session +session = requests.Session() +session.get('https://httpbin.org/cookies/set/sessionid/abc123') + +# Cookie is automatically sent in subsequent requests +response = session.get('https://httpbin.org/cookies') +data = response.json() +print(data['cookies']['sessionid']) # 'abc123' +``` + +### Manual Cookie Management + +```python +import requests +from requests.cookies import RequestsCookieJar + +# Create custom cookie jar +jar = RequestsCookieJar() +jar.set('custom_cookie', 'custom_value', domain='example.com') +jar.set('another_cookie', 'another_value') + +# Use jar with request +response = requests.get('https://example.com', cookies=jar) + +# Examine cookies +for name, value in jar.items(): + print(f"{name}: {value}") + +# Get cookies for specific domain +domain_cookies = jar.get_dict(domain='example.com') +print(domain_cookies) +``` + +### Cookie Jar Operations + +```python +import requests + +# Get cookies from response +response = requests.get('https://httpbin.org/cookies/set/foo/bar') +jar = response.cookies + +# Cookie jar behaves like a dict +print(jar['foo']) # 'bar' +print(jar.get('foo')) # 'bar' +print(list(jar.keys())) # ['foo'] +print(list(jar.values())) # ['bar'] +print(list(jar.items())) # [('foo', 'bar')] + +# Copy cookie jar +new_jar = jar.copy() + +# Update from dict +jar.update({'baz': 'qux'}) + +# Check domains and paths +print(jar.list_domains()) +print(jar.list_paths()) +print(jar.multiple_domains()) +``` + +### Cookie Utilities + +```python +import requests +from requests.cookies import cookiejar_from_dict, merge_cookies + +# Create jar from dict +cookie_dict = {'session': 'abc123', 'user': 'john'} +jar = cookiejar_from_dict(cookie_dict) + +# Use with session +session = requests.Session() +session.cookies = jar + +# Merge cookies +additional_cookies = {'theme': 'dark', 'lang': 'en'} +merge_cookies(jar, additional_cookies) + +response = session.get('https://httpbin.org/cookies') +``` + +### Advanced Cookie Handling + +```python +import requests +from requests.cookies import RequestsCookieJar + +def manage_cookies(): + jar = RequestsCookieJar() + + # Set cookies with attributes + jar.set('secure_cookie', 'secret_value', + domain='api.example.com', + path='/secure', + secure=True) + + # Set session cookie (no expiry) + jar.set('session_id', 'sess_123456') + + # Check cookie existence + if 'session_id' in jar: + print("Session cookie found") + + # Get cookie with domain/path filtering + secure_value = jar.get('secure_cookie', + domain='api.example.com', + path='/secure') + + # Remove specific cookie + if 'old_cookie' in jar: + del jar['old_cookie'] + + return jar + +# Use custom jar +jar = manage_cookies() +response = requests.get('https://api.example.com/secure/data', cookies=jar) +``` + +## Cookie Security + +### Secure Cookie Practices + +```python +import requests +from requests.cookies import RequestsCookieJar + +# Always use HTTPS for sensitive cookies +jar = RequestsCookieJar() +jar.set('auth_token', 'sensitive_token', + domain='secure-api.com', + secure=True, # Only send over HTTPS + httponly=True) # Not accessible via JavaScript + +# Use with secure request +response = requests.get('https://secure-api.com/data', cookies=jar) +``` + +### Cookie Domain Validation + +```python +import requests + +def safe_cookie_handling(url, cookies): + """Safely handle cookies with domain validation.""" + session = requests.Session() + + # Let requests handle cookie domain validation + response = session.get(url, cookies=cookies) + + # Check which cookies were actually sent + request_cookies = response.request.headers.get('Cookie', '') + print(f"Cookies sent: {request_cookies}") + + return response + +# Example usage +cookies = {'token': 'abc123'} +response = safe_cookie_handling('https://api.example.com', cookies) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/exceptions.md b/.tessl/tiles/tessl/pypi-requests/docs/exceptions.md new file mode 100644 index 000000000..f0ea58896 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/exceptions.md @@ -0,0 +1,430 @@ +# Exception Handling + +Comprehensive exception hierarchy for handling various error conditions that can occur during HTTP requests. The requests library provides specific exceptions for different types of errors, allowing for precise error handling. + +## Capabilities + +### Base Exception + +All requests exceptions inherit from the base RequestException class. + +```python { .api } +class RequestException(IOError): + """ + Base exception for all request-related errors. + + Attributes: + - response: Response object (if available) + - request: Request object that caused the exception + """ + + def __init__(self, *args, **kwargs): + """ + Initialize RequestException. + + Parameters: + - response: Response object (optional) + - request: Request object (optional) + """ +``` + +### HTTP Errors + +Exceptions related to HTTP status codes and server responses. + +```python { .api } +class HTTPError(RequestException): + """ + An HTTP error occurred. + + Raised by Response.raise_for_status() for 4xx and 5xx status codes. + """ + +class InvalidJSONError(RequestException): + """A JSON error occurred.""" + +class JSONDecodeError(InvalidJSONError): + """Couldn't decode the text into JSON.""" +``` + +### Connection Errors + +Exceptions related to network connectivity and connection issues. + +```python { .api } +class ConnectionError(RequestException): + """A connection error occurred.""" + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + +class SSLError(ConnectionError): + """An SSL error occurred.""" +``` + +### Timeout Errors + +Exceptions related to request timeouts. + +```python { .api } +class Timeout(RequestException): + """ + The request timed out. + + Catching this error will catch both ConnectTimeout and ReadTimeout. + """ + +class ConnectTimeout(ConnectionError, Timeout): + """ + The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" +``` + +### URL and Request Errors + +Exceptions related to malformed URLs and invalid requests. + +```python { .api } +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + +class MissingSchema(RequestException, ValueError): + """The URL scheme (e.g. http or https) is missing.""" + +class InvalidSchema(RequestException, ValueError): + """The URL scheme is invalid.""" + +class InvalidURL(RequestException, ValueError): + """The URL provided is invalid.""" + +class InvalidHeader(RequestException, ValueError): + """The header provided is invalid.""" + +class InvalidProxyURL(InvalidURL): + """The proxy URL provided is invalid.""" +``` + +### Redirect Errors + +Exceptions related to HTTP redirects. + +```python { .api } +class TooManyRedirects(RequestException): + """Too many redirects occurred.""" +``` + +### Content and Encoding Errors + +Exceptions related to response content processing. + +```python { .api } +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + +class ContentDecodingError(RequestException): + """Failed to decode response content.""" + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed.""" + +class RetryError(RequestException): + """Custom retries logic failed.""" + +class UnrewindableBodyError(RequestException): + """The request body cannot be rewound for a retry.""" +``` + +### Warning Classes + +Warning classes for non-fatal issues. + +```python { .api } +class RequestsWarning(Warning): + """Base warning for requests-related warnings.""" + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """Warning for file mode issues.""" + +class RequestsDependencyWarning(RequestsWarning): + """Warning for dependency-related issues.""" +``` + +## Usage Examples + +### Basic Exception Handling + +```python +import requests +from requests.exceptions import RequestException, HTTPError, ConnectionError, Timeout + +try: + response = requests.get('https://api.example.com/data', timeout=5) + response.raise_for_status() # Raises HTTPError for bad status codes + data = response.json() +except HTTPError as e: + print(f"HTTP error occurred: {e}") + print(f"Status code: {e.response.status_code}") +except ConnectionError as e: + print(f"Connection error occurred: {e}") +except Timeout as e: + print(f"Request timed out: {e}") +except RequestException as e: + print(f"An error occurred: {e}") +``` + +### Specific Exception Handling + +```python +import requests +from requests.exceptions import ( + ConnectTimeout, ReadTimeout, SSLError, + JSONDecodeError, TooManyRedirects +) + +def robust_api_call(url, max_retries=3): + for attempt in range(max_retries): + try: + response = requests.get(url, timeout=(5, 10)) # (connect, read) timeout + response.raise_for_status() + return response.json() + + except ConnectTimeout: + print(f"Connection timeout on attempt {attempt + 1}") + if attempt == max_retries - 1: + raise + + except ReadTimeout: + print(f"Read timeout on attempt {attempt + 1}") + if attempt == max_retries - 1: + raise + + except SSLError as e: + print(f"SSL error: {e}") + # SSL errors usually shouldn't be retried + raise + + except JSONDecodeError: + print("Response is not valid JSON") + # Try to return raw text instead + return response.text + + except TooManyRedirects: + print("Too many redirects") + raise + + except HTTPError as e: + if e.response.status_code >= 500: + # Server errors might be temporary, retry + print(f"Server error {e.response.status_code} on attempt {attempt + 1}") + if attempt == max_retries - 1: + raise + else: + # Client errors usually shouldn't be retried + print(f"Client error {e.response.status_code}") + raise + +result = robust_api_call('https://api.example.com/data') +``` + +### Connection Error Handling + +```python +import requests +from requests.exceptions import ConnectionError, ProxyError, SSLError + +def handle_connection_errors(url): + try: + response = requests.get(url) + return response + + except ProxyError as e: + print(f"Proxy error: {e}") + # Try without proxy + return requests.get(url, proxies={'http': '', 'https': ''}) + + except SSLError as e: + print(f"SSL error: {e}") + # Option 1: Use HTTP instead (not recommended for production) + # return requests.get(url.replace('https://', 'http://')) + + # Option 2: Disable SSL verification (not recommended for production) + # return requests.get(url, verify=False) + + # Option 3: Provide custom CA bundle + return requests.get(url, verify='/path/to/ca-bundle.crt') + + except ConnectionError as e: + print(f"Connection error: {e}") + raise # Re-raise if no recovery is possible +``` + +### URL and Request Error Handling + +```python +import requests +from requests.exceptions import ( + URLRequired, MissingSchema, InvalidSchema, + InvalidURL, InvalidHeader +) + +def validate_and_request(url, headers=None): + try: + response = requests.get(url, headers=headers) + return response + + except URLRequired: + raise ValueError("URL is required") + + except MissingSchema: + # Try adding https:// + return requests.get(f"https://{url}") + + except InvalidSchema as e: + print(f"Invalid URL scheme: {e}") + raise ValueError(f"URL must start with http:// or https://") + + except InvalidURL as e: + print(f"Invalid URL: {e}") + raise ValueError("Please provide a valid URL") + + except InvalidHeader as e: + print(f"Invalid header: {e}") + raise ValueError("Please check header format") + +# Example usage +try: + response = validate_and_request("example.com") # Missing schema +except ValueError as e: + print(f"Validation error: {e}") +``` + +### JSON Error Handling + +```python +import requests +from requests.exceptions import JSONDecodeError + +def safe_json_request(url): + try: + response = requests.get(url) + response.raise_for_status() + + # Try to parse JSON + return response.json() + + except JSONDecodeError as e: + print(f"JSON decode error: {e}") + print(f"Response content: {response.text[:200]}...") # First 200 chars + + # Return raw text or None based on content type + content_type = response.headers.get('content-type', '') + if 'json' in content_type.lower(): + # Expected JSON but got invalid JSON + raise ValueError("Expected JSON response but got invalid JSON") + else: + # Not JSON, return text + return response.text + +data = safe_json_request('https://api.example.com/data') +``` + +### Comprehensive Error Handling Pattern + +```python +import requests +from requests.exceptions import RequestException +import time + +def reliable_request(url, max_retries=3, backoff_factor=1): + """ + Make a reliable HTTP request with exponential backoff retry logic. + """ + last_exception = None + + for attempt in range(max_retries): + try: + response = requests.get(url, timeout=(5, 30)) + response.raise_for_status() + return response + + except requests.exceptions.ConnectTimeout: + print(f"Connect timeout on attempt {attempt + 1}") + last_exception = sys.exc_info()[1] + + except requests.exceptions.ReadTimeout: + print(f"Read timeout on attempt {attempt + 1}") + last_exception = sys.exc_info()[1] + + except requests.exceptions.HTTPError as e: + if 500 <= e.response.status_code < 600: + # Server error - retry + print(f"Server error {e.response.status_code} on attempt {attempt + 1}") + last_exception = e + else: + # Client error - don't retry + raise + + except requests.exceptions.ConnectionError: + print(f"Connection error on attempt {attempt + 1}") + last_exception = sys.exc_info()[1] + + except RequestException as e: + # Other request exceptions + print(f"Request exception on attempt {attempt + 1}: {e}") + last_exception = e + + # Wait before retrying (exponential backoff) + if attempt < max_retries - 1: + wait_time = backoff_factor * (2 ** attempt) + print(f"Waiting {wait_time} seconds before retry...") + time.sleep(wait_time) + + # All retries failed + raise last_exception + +# Usage +try: + response = reliable_request('https://api.example.com/unreliable-endpoint') + data = response.json() +except Exception as e: + print(f"Request ultimately failed: {e}") +``` + +### Exception Information Access + +```python +import requests +from requests.exceptions import HTTPError, RequestException + +try: + response = requests.get('https://httpbin.org/status/404') + response.raise_for_status() +except HTTPError as e: + print(f"HTTP Error: {e}") + print(f"Status Code: {e.response.status_code}") + print(f"Response Text: {e.response.text}") + print(f"Request URL: {e.request.url}") + print(f"Request Method: {e.request.method}") +except RequestException as e: + print(f"Request Exception: {e}") + if hasattr(e, 'response') and e.response is not None: + print(f"Response Status: {e.response.status_code}") + if hasattr(e, 'request') and e.request is not None: + print(f"Request URL: {e.request.url}") +``` + +## Best Practices + +1. **Always catch specific exceptions** before more general ones +2. **Use `response.raise_for_status()`** to automatically raise HTTPError for bad status codes +3. **Set appropriate timeouts** to avoid hanging requests +4. **Implement retry logic** for transient errors (timeouts, 5xx errors) +5. **Don't retry client errors** (4xx status codes) as they indicate request problems +6. **Log exception details** for debugging +7. **Provide meaningful error messages** to users +8. **Handle SSL errors carefully** - avoid disabling verification in production \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/hooks.md b/.tessl/tiles/tessl/pypi-requests/docs/hooks.md new file mode 100644 index 000000000..fd81a40ec --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/hooks.md @@ -0,0 +1,471 @@ +# Event Hooks + +Event hook system that allows custom functions to be called at specific points during request processing. Hooks provide a way to modify requests and responses or implement custom logging and monitoring. + +## Capabilities + +### Hook System + +The requests library provides a simple but powerful hook system for intercepting and modifying HTTP requests and responses. + +```python { .api } +# Available hook events +HOOKS: list[str] # ['response'] + +def default_hooks() -> dict: + """ + Return the default hooks structure. + + Returns: + Dict with default hook configuration + """ + +def dispatch_hook(key: str, hooks: dict, hook_data, **kwargs): + """ + Execute hooks for a given event. + + Parameters: + - key: Hook event name + - hooks: Hook configuration dict + - hook_data: Data to pass to hook functions + - **kwargs: Additional arguments for hooks + + Returns: + Result from hook execution + """ +``` + +### Hook Events + +Currently, requests supports one hook event: + +- **response**: Called after receiving a response, allows modifying the Response object + +## Usage Examples + +### Basic Hook Usage + +```python +import requests + +def response_hook(response, *args, **kwargs): + """Custom response hook function.""" + print(f"Received response: {response.status_code} from {response.url}") + # Optionally modify the response + response.custom_processed = True + return response + +# Add hook to single request +response = requests.get('https://httpbin.org/get', + hooks={'response': response_hook}) + +print(hasattr(response, 'custom_processed')) # True +``` + +### Multiple Hooks + +```python +import requests + +def log_hook(response, *args, **kwargs): + """Log response details.""" + print(f"LOG: {response.request.method} {response.url} -> {response.status_code}") + return response + +def timing_hook(response, *args, **kwargs): + """Add timing information.""" + print(f"TIMING: Request took {response.elapsed.total_seconds():.3f} seconds") + return response + +def validation_hook(response, *args, **kwargs): + """Validate response.""" + if response.status_code >= 400: + print(f"WARNING: Error response {response.status_code}") + return response + +# Multiple hooks for the same event +hooks = { + 'response': [log_hook, timing_hook, validation_hook] +} + +response = requests.get('https://httpbin.org/status/404', hooks=hooks) +# Prints: +# LOG: GET https://httpbin.org/status/404 -> 404 +# TIMING: Request took 0.123 seconds +# WARNING: Error response 404 +``` + +### Session-Level Hooks + +```python +import requests + +def session_response_hook(response, *args, **kwargs): + """Hook applied to all session requests.""" + # Add custom header to all responses + response.headers['X-Custom-Processed'] = 'true' + + # Log all requests + print(f"Session request: {response.request.method} {response.url}") + + return response + +# Add hook to session - applies to all requests +session = requests.Session() +session.hooks['response'].append(session_response_hook) + +# All requests through this session will trigger the hook +response1 = session.get('https://httpbin.org/get') +response2 = session.post('https://httpbin.org/post', json={'key': 'value'}) + +print(response1.headers['X-Custom-Processed']) # 'true' +print(response2.headers['X-Custom-Processed']) # 'true' +``` + +### Authentication Hook + +```python +import requests +import hashlib +import time + +def custom_auth_hook(response, *args, **kwargs): + """Custom authentication logic.""" + if response.status_code == 401: + print("Authentication required - could trigger token refresh") + # In real implementation, might refresh token and retry request + return response + +def rate_limit_hook(response, *args, **kwargs): + """Handle rate limiting.""" + if response.status_code == 429: + retry_after = response.headers.get('Retry-After', '60') + print(f"Rate limited. Retry after {retry_after} seconds") + # Could implement automatic retry with backoff + return response + +# Combine authentication and rate limiting hooks +auth_hooks = { + 'response': [custom_auth_hook, rate_limit_hook] +} + +session = requests.Session() +session.hooks = auth_hooks + +response = session.get('https://httpbin.org/status/429') +# Prints: Rate limited. Retry after 60 seconds +``` + +### Request Modification Hook + +```python +import requests + +def add_user_agent_hook(response, *args, **kwargs): + """Add custom user agent information to response for logging.""" + original_ua = response.request.headers.get('User-Agent', 'Unknown') + print(f"Request made with User-Agent: {original_ua}") + + # Add metadata to response for later use + response.user_agent_info = { + 'original_ua': original_ua, + 'timestamp': time.time() + } + return response + +# Apply hook +response = requests.get('https://httpbin.org/user-agent', + hooks={'response': add_user_agent_hook}) + +print(response.user_agent_info) +``` + +### Error Handling Hook + +```python +import requests +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +def error_logging_hook(response, *args, **kwargs): + """Log errors and add context.""" + if response.status_code >= 400: + logger.error(f"HTTP Error {response.status_code}: {response.url}") + logger.error(f"Response: {response.text[:200]}...") + + # Add error context to response + response.error_logged = True + response.error_timestamp = time.time() + + return response + +def retry_header_hook(response, *args, **kwargs): + """Extract retry information from headers.""" + if response.status_code in [429, 503]: + retry_after = response.headers.get('Retry-After') + if retry_after: + response.retry_after_seconds = int(retry_after) + logger.info(f"Server suggests retry after {retry_after} seconds") + + return response + +# Error handling hook chain +error_hooks = { + 'response': [error_logging_hook, retry_header_hook] +} + +response = requests.get('https://httpbin.org/status/503', hooks=error_hooks) +print(f"Error logged: {getattr(response, 'error_logged', False)}") +``` + +### Performance Monitoring Hook + +```python +import requests +import time + +class PerformanceMonitor: + """Performance monitoring via hooks.""" + + def __init__(self): + self.stats = {} + + def response_hook(self, response, *args, **kwargs): + """Monitor response performance.""" + url = response.url + elapsed = response.elapsed.total_seconds() + status = response.status_code + size = len(response.content) + + # Track stats + if url not in self.stats: + self.stats[url] = [] + + self.stats[url].append({ + 'elapsed': elapsed, + 'status': status, + 'size': size, + 'timestamp': time.time() + }) + + print(f"PERF: {url} -> {status} ({elapsed:.3f}s, {size} bytes)") + return response + + def get_stats(self): + """Get performance statistics.""" + return self.stats + +# Use performance monitor +monitor = PerformanceMonitor() + +session = requests.Session() +session.hooks['response'].append(monitor.response_hook) + +# Make several requests +urls = [ + 'https://httpbin.org/get', + 'https://httpbin.org/json', + 'https://httpbin.org/html' +] + +for url in urls: + response = session.get(url) + +# View stats +stats = monitor.get_stats() +for url, measurements in stats.items(): + avg_time = sum(m['elapsed'] for m in measurements) / len(measurements) + print(f"Average time for {url}: {avg_time:.3f}s") +``` + +### Custom Response Processing Hook + +```python +import requests +import json + +def json_response_hook(response, *args, **kwargs): + """Automatically parse JSON responses.""" + content_type = response.headers.get('content-type', '') + + if 'application/json' in content_type: + try: + response.parsed_json = response.json() + response.json_parsed = True + except ValueError: + response.json_parsed = False + response.parsed_json = None + else: + response.json_parsed = False + response.parsed_json = None + + return response + +def xml_response_hook(response, *args, **kwargs): + """Handle XML responses.""" + content_type = response.headers.get('content-type', '') + + if 'xml' in content_type: + response.is_xml = True + # Could parse XML here with lxml or xml module + else: + response.is_xml = False + + return response + +# Content processing hooks +content_hooks = { + 'response': [json_response_hook, xml_response_hook] +} + +response = requests.get('https://httpbin.org/json', hooks=content_hooks) +print(f"JSON parsed: {response.json_parsed}") +print(f"Data: {response.parsed_json}") +``` + +### Hook Registration Patterns + +```python +import requests + +# Pattern 1: Direct function assignment +def my_hook(response, *args, **kwargs): + return response + +hooks = {'response': my_hook} + +# Pattern 2: List of functions +hooks = {'response': [hook1, hook2, hook3]} + +# Pattern 3: Adding to existing hooks +session = requests.Session() +session.hooks['response'].append(my_hook) + +# Pattern 4: Replacing all hooks +session.hooks['response'] = [my_hook] + +# Pattern 5: Using Request object +req = requests.Request('GET', 'https://httpbin.org/get', hooks=hooks) +prepared = req.prepare() + +with requests.Session() as session: + response = session.send(prepared) +``` + +## Hook Best Practices + +### Hook Function Signature + +```python +def my_hook(response, *args, **kwargs): + """ + Standard hook function signature. + + Parameters: + - response: Response object being processed + - *args: Additional positional arguments + - **kwargs: Additional keyword arguments + + Returns: + Modified or original Response object + """ + # Process response + return response +``` + +### Error Handling in Hooks + +```python +def safe_hook(response, *args, **kwargs): + """Hook with proper error handling.""" + try: + # Hook logic here + response.custom_data = process_response(response) + except Exception as e: + # Log error but don't break the request + print(f"Hook error: {e}") + response.hook_error = str(e) + + return response +``` + +### Performance Considerations + +```python +def efficient_hook(response, *args, **kwargs): + """Efficient hook implementation.""" + # Only process if needed + if should_process(response): + # Minimal processing + response.processed = True + + return response + +def should_process(response): + """Determine if processing is needed.""" + return response.headers.get('content-type') == 'application/json' +``` + +## Hook Limitations + +1. **Limited Events**: Only 'response' hook is currently supported +2. **No Request Hooks**: Cannot modify requests before sending +3. **Exception Handling**: Hook exceptions can break request processing +4. **Performance Impact**: Hooks add processing overhead +5. **State Management**: Hooks are stateless - use closures or classes for state + +## Advanced Hook Patterns + +### Hook Factory + +```python +def create_logging_hook(logger_name): + """Factory function to create logging hooks.""" + import logging + logger = logging.getLogger(logger_name) + + def logging_hook(response, *args, **kwargs): + logger.info(f"{response.request.method} {response.url} -> {response.status_code}") + return response + + return logging_hook + +# Create specialized hooks +api_hook = create_logging_hook('api_client') +web_hook = create_logging_hook('web_scraper') + +# Use different hooks for different purposes +api_session = requests.Session() +api_session.hooks['response'].append(api_hook) + +web_session = requests.Session() +web_session.hooks['response'].append(web_hook) +``` + +### Conditional Hooks + +```python +def conditional_hook(condition_func): + """Create a hook that only runs when condition is met.""" + def decorator(hook_func): + def wrapper(response, *args, **kwargs): + if condition_func(response): + return hook_func(response, *args, **kwargs) + return response + return wrapper + return decorator + +# Conditional hook decorators +@conditional_hook(lambda r: r.status_code >= 400) +def error_only_hook(response, *args, **kwargs): + print(f"Error response: {response.status_code}") + return response + +@conditional_hook(lambda r: 'json' in r.headers.get('content-type', '')) +def json_only_hook(response, *args, **kwargs): + print("Processing JSON response") + return response +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/http-methods.md b/.tessl/tiles/tessl/pypi-requests/docs/http-methods.md new file mode 100644 index 000000000..7e839da81 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/http-methods.md @@ -0,0 +1,229 @@ +# HTTP Methods + +Core HTTP method functions that provide the primary interface for making HTTP requests. These functions handle the most common HTTP verbs and automatically manage sessions internally. + +## Capabilities + +### Main Request Function + +The core request function that all other HTTP method functions use internally. Provides full control over HTTP method and request parameters. + +```python { .api } +def request(method: str, url: str, **kwargs) -> Response: + """ + Constructs and sends a Request. + + Parameters: + - method: HTTP method ('GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS') + - url: URL for the request + - params: dict, list of tuples or bytes for query string parameters + - data: dict, list of tuples, bytes, or file-like object for request body + - json: JSON serializable Python object for request body + - headers: dict of HTTP headers + - cookies: dict or CookieJar object + - files: dict of file-like objects for multipart encoding + - auth: auth tuple or auth handler instance + - timeout: float or (connect timeout, read timeout) tuple in seconds + - allow_redirects: bool to enable/disable redirects + - proxies: dict mapping protocol to proxy URL + - verify: bool or path to CA bundle for SSL verification + - stream: bool to download response content immediately + - cert: path to SSL client cert file or (cert, key) tuple + + Returns: + Response object + """ +``` + +### GET Requests + +Send HTTP GET requests to retrieve data from servers. + +```python { .api } +def get(url: str, params=None, **kwargs) -> Response: + """ + Sends a GET request. + + Parameters: + - url: URL for the request + - params: dict, list of tuples or bytes for query string parameters + - **kwargs: optional arguments that request() accepts + + Returns: + Response object + """ +``` + +Usage example: + +```python +import requests + +# Simple GET request +response = requests.get('https://api.github.com/users/octocat') + +# GET with query parameters +params = {'q': 'python', 'sort': 'stars'} +response = requests.get('https://api.github.com/search/repositories', params=params) + +# GET with headers and authentication +headers = {'User-Agent': 'MyApp/1.0'} +response = requests.get('https://api.example.com/data', + headers=headers, + auth=('username', 'password')) +``` + +### POST Requests + +Send HTTP POST requests to submit data to servers. + +```python { .api } +def post(url: str, data=None, json=None, **kwargs) -> Response: + """ + Sends a POST request. + + Parameters: + - url: URL for the request + - data: dict, list of tuples, bytes, or file-like object for request body + - json: JSON serializable Python object for request body + - **kwargs: optional arguments that request() accepts + + Returns: + Response object + """ +``` + +Usage example: + +```python +import requests + +# POST with form data +data = {'key1': 'value1', 'key2': 'value2'} +response = requests.post('https://httpbin.org/post', data=data) + +# POST with JSON data +json_data = {'user': 'john', 'age': 30} +response = requests.post('https://api.example.com/users', json=json_data) + +# POST with file upload +files = {'file': open('document.pdf', 'rb')} +response = requests.post('https://httpbin.org/post', files=files) +``` + +### PUT Requests + +Send HTTP PUT requests to create or update resources. + +```python { .api } +def put(url: str, data=None, **kwargs) -> Response: + """ + Sends a PUT request. + + Parameters: + - url: URL for the request + - data: dict, list of tuples, bytes, or file-like object for request body + - **kwargs: optional arguments that request() accepts + + Returns: + Response object + """ +``` + +### PATCH Requests + +Send HTTP PATCH requests to partially update resources. + +```python { .api } +def patch(url: str, data=None, **kwargs) -> Response: + """ + Sends a PATCH request. + + Parameters: + - url: URL for the request + - data: dict, list of tuples, bytes, or file-like object for request body + - **kwargs: optional arguments that request() accepts + + Returns: + Response object + """ +``` + +### DELETE Requests + +Send HTTP DELETE requests to remove resources. + +```python { .api } +def delete(url: str, **kwargs) -> Response: + """ + Sends a DELETE request. + + Parameters: + - url: URL for the request + - **kwargs: optional arguments that request() accepts + + Returns: + Response object + """ +``` + +### HEAD Requests + +Send HTTP HEAD requests to retrieve headers without response body. + +```python { .api } +def head(url: str, **kwargs) -> Response: + """ + Sends a HEAD request. + + Parameters: + - url: URL for the request + - **kwargs: optional arguments that request() accepts + Note: allow_redirects defaults to False for HEAD requests + + Returns: + Response object with empty content + """ +``` + +### OPTIONS Requests + +Send HTTP OPTIONS requests to determine allowed methods and capabilities. + +```python { .api } +def options(url: str, **kwargs) -> Response: + """ + Sends an OPTIONS request. + + Parameters: + - url: URL for the request + - **kwargs: optional arguments that request() accepts + + Returns: + Response object + """ +``` + +## Common Parameters + +All HTTP method functions accept these common optional parameters: + +- **headers**: `Dict[str, str]` - HTTP headers to send +- **auth**: `AuthType` - Authentication tuple or handler +- **timeout**: `Union[float, Tuple[float, float]]` - Request timeout in seconds +- **proxies**: `Dict[str, str]` - Proxy configuration +- **verify**: `Union[bool, str]` - SSL certificate verification +- **cert**: `Union[str, Tuple[str, str]]` - Client certificate +- **stream**: `bool` - Stream download response content +- **allow_redirects**: `bool` - Follow redirects (default True, except HEAD) +- **cookies**: `CookiesType` - Cookies to send + +## Error Handling + +All HTTP method functions can raise these exceptions: + +- **RequestException**: Base exception for all request-related errors +- **HTTPError**: 4xx or 5xx status codes (when raise_for_status() is called) +- **ConnectionError**: Connection-related errors +- **Timeout**: Request timeout errors +- **URLRequired**: Invalid or missing URL \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/index.md b/.tessl/tiles/tessl/pypi-requests/docs/index.md new file mode 100644 index 000000000..b4e0bece4 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/index.md @@ -0,0 +1,262 @@ +# Requests + +Python HTTP for Humans - an elegant and simple HTTP library that abstracts the complexities of making HTTP requests behind a beautiful, simple API. Requests allows you to send HTTP/1.1 requests extremely easily with features like automatic content decoding, connection pooling, cookie persistence, authentication, and comprehensive SSL verification. + +## Package Information + +- **Package Name**: requests +- **Language**: Python +- **Installation**: `pip install requests` +- **Version**: 2.32.4 + +## Core Imports + +```python +import requests +``` + +Individual components: + +```python +from requests import Session, Request, PreparedRequest, Response, codes +from requests.auth import HTTPBasicAuth, HTTPDigestAuth, HTTPProxyAuth, AuthBase +from requests.exceptions import ( + RequestException, HTTPError, ConnectionError, Timeout, ConnectTimeout, + ReadTimeout, URLRequired, TooManyRedirects, SSLError, JSONDecodeError +) +from requests.adapters import HTTPAdapter, BaseAdapter +from requests.structures import CaseInsensitiveDict, LookupDict +from requests.cookies import RequestsCookieJar +``` + +## Basic Usage + +```python +import requests + +# Simple GET request +response = requests.get('https://api.github.com/user', auth=('user', 'pass')) +print(response.status_code) +print(response.json()) + +# POST request with JSON data +data = {'key': 'value'} +response = requests.post('https://httpbin.org/post', json=data) + +# Using sessions for persistent settings +session = requests.Session() +session.auth = ('user', 'pass') +session.headers.update({'Custom-Header': 'value'}) + +response = session.get('https://api.example.com/data') +``` + +## Architecture + +Requests is built around several key components: + +- **HTTP Methods**: Simple functions (get, post, put, etc.) that create and send requests +- **Session**: Persistent configuration and connection pooling across requests +- **Request/PreparedRequest**: Request objects representing HTTP requests before sending +- **Response**: Response objects containing server responses with convenient access methods +- **Authentication**: Pluggable authentication handlers for various auth schemes +- **Adapters**: Transport adapters that handle the actual HTTP communication +- **Exceptions**: Comprehensive exception hierarchy for different error conditions + +## Capabilities + +### HTTP Methods + +Core HTTP method functions for making requests with various verbs. These are the primary interface most users interact with. + +```python { .api } +def request(method: str, url: str, **kwargs) -> Response: ... +def get(url: str, params=None, **kwargs) -> Response: ... +def post(url: str, data=None, json=None, **kwargs) -> Response: ... +def put(url: str, data=None, **kwargs) -> Response: ... +def patch(url: str, data=None, **kwargs) -> Response: ... +def delete(url: str, **kwargs) -> Response: ... +def head(url: str, **kwargs) -> Response: ... +def options(url: str, **kwargs) -> Response: ... +``` + +[HTTP Methods](./http-methods.md) + +### Sessions + +Session objects provide persistent configuration, connection pooling, and cookie persistence across multiple requests. + +```python { .api } +class Session: + def __init__(self): ... + def request(self, method: str, url: str, **kwargs) -> Response: ... + def get(self, url: str, **kwargs) -> Response: ... + def post(self, url: str, **kwargs) -> Response: ... + def close(self): ... + +def session() -> Session: ... +``` + +[Sessions](./sessions.md) + +### Request and Response Objects + +Core objects representing HTTP requests and responses with full control over request preparation and response handling. + +```python { .api } +class Request: + def __init__(self, method=None, url=None, headers=None, files=None, + data=None, params=None, auth=None, cookies=None, + hooks=None, json=None): ... + def prepare(self) -> PreparedRequest: ... + +class Response: + content: bytes + text: str + status_code: int + headers: dict + cookies: dict + url: str + def json(self, **kwargs) -> dict: ... + def raise_for_status(self): ... +``` + +[Request and Response Objects](./models.md) + +### Authentication + +Authentication handlers for various HTTP authentication schemes including Basic, Digest, and custom authentication. + +```python { .api } +class HTTPBasicAuth: + def __init__(self, username: str, password: str): ... + +class HTTPDigestAuth: + def __init__(self, username: str, password: str): ... + +class HTTPProxyAuth(HTTPBasicAuth): ... +``` + +[Authentication](./authentication.md) + +### Exception Handling + +Comprehensive exception hierarchy for handling various error conditions that can occur during HTTP requests. + +```python { .api } +class RequestException(IOError): ... +class HTTPError(RequestException): ... +class ConnectionError(RequestException): ... +class Timeout(RequestException): ... +class ConnectTimeout(ConnectionError, Timeout): ... +class ReadTimeout(Timeout): ... +class URLRequired(RequestException): ... +class TooManyRedirects(RequestException): ... +class SSLError(ConnectionError): ... +``` + +[Exception Handling](./exceptions.md) + +### Status Codes + +Convenient access to HTTP status codes through named constants and lookup functionality. + +```python { .api } +codes: dict # Lookup dict for status codes +# Usage: codes.ok == 200, codes['not_found'] == 404 +``` + +[Status Codes](./status-codes.md) + +### Cookie Handling + +Cookie management functionality providing a dict-like interface for handling HTTP cookies with compatibility for both client and server-side cookie operations. + +```python { .api } +class RequestsCookieJar: + def __init__(self, policy=None): ... + def get(self, name: str, default=None, domain=None, path=None) -> str: ... + def set(self, name: str, value: str, **kwargs): ... + def __getitem__(self, name: str) -> str: ... + def __setitem__(self, name: str, value: str): ... + +def cookiejar_from_dict(cookie_dict: dict, cookiejar=None, overwrite=True): ... +def merge_cookies(cookiejar, cookies): ... +``` + +[Cookie Handling](./cookies.md) + +### Transport Adapters + +Transport adapters handle the actual HTTP communication, providing the interface between requests and underlying HTTP libraries. + +```python { .api } +class BaseAdapter: + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None) -> Response: ... + def close(self): ... + +class HTTPAdapter(BaseAdapter): + def __init__(self, pool_connections=10, pool_maxsize=10, max_retries=0, + pool_block=False): ... + def send(self, request, **kwargs) -> Response: ... + def mount(self, prefix: str, adapter): ... +``` + +[Transport Adapters](./adapters.md) + +### Data Structures + +Data structure classes that provide enhanced dictionary interfaces with special behaviors for HTTP-related operations. + +```python { .api } +class CaseInsensitiveDict(dict): + def __init__(self, data=None, **kwargs): ... + def __getitem__(self, key): ... + def __setitem__(self, key, value): ... + def copy(self) -> 'CaseInsensitiveDict': ... + +class LookupDict(dict): + def __init__(self, name=None): ... + def __getitem__(self, key): ... + def get(self, key, default=None): ... +``` + +[Data Structures](./structures.md) + +### Event Hooks + +Event hook system that allows custom functions to be called at specific points during request processing. + +```python { .api } +HOOKS: list[str] # ['response'] + +def default_hooks() -> dict: ... +def dispatch_hook(key: str, hooks: dict, hook_data, **kwargs): ... +``` + +[Event Hooks](./hooks.md) + +## Types + +```python { .api } +from typing import Dict, Optional, Union, Any, List, Tuple, IO +from datetime import timedelta + +# Common type aliases +JSONType = Union[Dict[str, Any], List[Any], str, int, float, bool, None] +AuthType = Union[Tuple[str, str], HTTPBasicAuth, HTTPDigestAuth, HTTPProxyAuth, AuthBase] +CookiesType = Union[Dict[str, str], RequestsCookieJar] +HeadersType = Union[Dict[str, str], CaseInsensitiveDict] +ParamsType = Union[Dict[str, str], List[Tuple[str, str]], bytes] +DataType = Union[Dict[str, Any], List[Tuple[str, str]], str, bytes, IO] +FilesType = Dict[str, Union[str, bytes, IO, Tuple[str, Union[str, bytes, IO]], + Tuple[str, Union[str, bytes, IO], str], + Tuple[str, Union[str, bytes, IO], str, Dict[str, str]]]] +ProxiesType = Dict[str, str] +TimeoutType = Union[float, Tuple[float, float]] +VerifyType = Union[bool, str] +CertType = Union[str, Tuple[str, str]] +HooksType = Dict[str, List[callable]] +AdapterType = BaseAdapter +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/models.md b/.tessl/tiles/tessl/pypi-requests/docs/models.md new file mode 100644 index 000000000..d1deeef14 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/models.md @@ -0,0 +1,378 @@ +# Request and Response Objects + +Core objects that represent HTTP requests and responses, providing full control over request preparation and response handling. These objects form the foundation of the requests library's functionality. + +## Capabilities + +### Request Class + +User-created Request objects that represent HTTP requests before they are sent. + +```python { .api } +class Request: + """ + A user-created Request object. + + Used to prepare a PreparedRequest, which is sent to the server. + """ + + def __init__(self, method=None, url=None, headers=None, files=None, + data=None, params=None, auth=None, cookies=None, + hooks=None, json=None): + """ + Initialize a Request object. + + Parameters: + - method: HTTP method to use ('GET', 'POST', etc.) + - url: URL to send the request to + - headers: dict of headers to send + - files: dict of {filename: fileobject} for multipart upload + - data: request body data (dict, list of tuples, bytes, or file-like) + - params: URL parameters to append (dict or list of tuples) + - auth: authentication tuple or handler + - cookies: cookies to send (dict or CookieJar) + - hooks: event hooks dict + - json: JSON serializable object for request body + """ + + def prepare(self) -> 'PreparedRequest': + """ + Prepare the request for sending. + + Returns: + PreparedRequest object ready to send + """ +``` + +### PreparedRequest Class + +Fully prepared request objects containing the exact bytes that will be sent to the server. + +```python { .api } +class PreparedRequest: + """ + The fully mutable PreparedRequest object. + + Contains the exact bytes that will be sent to the server. + Should not be instantiated manually. + + Attributes: + - method: HTTP method (str) + - url: Full URL (str) + - headers: Request headers (CaseInsensitiveDict) + - body: Request body (bytes or str or None) + - hooks: Event hooks (dict) + - _cookies: CookieJar for cookie header generation + - _body_position: Position marker for rewindable request bodies + """ + + def __init__(self): + """Initialize a PreparedRequest object.""" + + def prepare(self, method=None, url=None, files=None, data=None, + headers=None, params=None, auth=None, cookies=None, + hooks=None, json=None): + """ + Prepare all aspects of the request. + + Parameters: Same as Request.__init__ + """ + + def prepare_method(self, method: str): + """Prepare the HTTP method.""" + + def prepare_url(self, url: str, params): + """Prepare the URL with parameters.""" + + def prepare_headers(self, headers): + """Prepare the headers.""" + + def prepare_cookies(self, cookies): + """Prepare the cookies.""" + + def prepare_body(self, data, files, json=None): + """Prepare the request body.""" + + def prepare_auth(self, auth, url=''): + """Prepare authentication.""" + + def prepare_content_length(self, body): + """Prepare Content-Length header.""" + + def prepare_hooks(self, hooks): + """Prepare event hooks.""" + + def copy(self) -> 'PreparedRequest': + """Create a copy of the PreparedRequest.""" +``` + +### Response Class + +Response objects containing server responses to HTTP requests. + +```python { .api } +class Response: + """ + The Response object contains a server's response to an HTTP request. + """ + + # Response attributes + content: bytes # Response content as bytes + text: str # Response content as text + encoding: str # Response encoding + status_code: int # HTTP status code + headers: 'CaseInsensitiveDict' # Response headers + cookies: 'RequestsCookieJar' # Response cookies + url: str # Final URL location of response + history: list['Response'] # List of Response objects (redirects) + reason: str # Textual reason of response (e.g., 'OK', 'Not Found') + request: 'PreparedRequest' # PreparedRequest that generated this response + elapsed: 'timedelta' # Time elapsed between request and response + raw: object # Raw response object (urllib3.HTTPResponse) + + def __init__(self): + """Initialize a Response object.""" + + def __enter__(self): + """Context manager entry.""" + + def __exit__(self, *args): + """Context manager exit.""" + + def __bool__(self) -> bool: + """Boolean evaluation based on status code.""" + + def __nonzero__(self) -> bool: + """Boolean evaluation for Python 2 compatibility.""" + + def __iter__(self): + """Iterate over response content in chunks.""" + + @property + def ok(self) -> bool: + """True if status code is less than 400.""" + + @property + def is_redirect(self) -> bool: + """True if response is a redirect.""" + + @property + def is_permanent_redirect(self) -> bool: + """True if response is a permanent redirect.""" + + @property + def next(self): + """Returns parsed header links if present.""" + + @property + def apparent_encoding(self) -> str: + """Apparent encoding of response content.""" + + def iter_content(self, chunk_size: int = 1, decode_unicode: bool = False): + """ + Iterate over response data in chunks. + + Parameters: + - chunk_size: Size of chunks to read + - decode_unicode: Whether to decode content to unicode + + Yields: + Chunks of response content + """ + + def iter_lines(self, chunk_size: int = 512, decode_unicode: bool = False, + delimiter=None): + """ + Iterate over response lines. + + Parameters: + - chunk_size: Size of chunks to read + - decode_unicode: Whether to decode content to unicode + - delimiter: Line delimiter + + Yields: + Lines from response content + """ + + def json(self, **kwargs) -> Union[dict, list]: + """ + Parse response content as JSON. + + Parameters: + - **kwargs: Arguments passed to json.loads() + + Returns: + Parsed JSON data + + Raises: + JSONDecodeError: If response is not valid JSON + """ + + @property + def links(self) -> dict: + """Returns parsed header links.""" + + def raise_for_status(self): + """ + Raise HTTPError for bad responses (4xx or 5xx status codes). + + Raises: + HTTPError: If status code indicates an error + """ + + def close(self): + """Release the connection back to the pool.""" +``` + +## Usage Examples + +### Basic Request Creation + +```python +import requests + +# Create a request object +req = requests.Request('GET', 'https://api.github.com/user', + auth=('username', 'password')) + +# Prepare the request +prepared = req.prepare() + +# Send the prepared request +with requests.Session() as s: + response = s.send(prepared) + print(response.status_code) +``` + +### Working with Response Objects + +```python +import requests + +response = requests.get('https://api.github.com/users/octocat') + +# Access response properties +print(f"Status: {response.status_code}") +print(f"Headers: {response.headers}") +print(f"URL: {response.url}") +print(f"Encoding: {response.encoding}") + +# Check if request was successful +if response.ok: + print("Request successful") + +# Parse JSON response +try: + data = response.json() + print(f"User: {data['login']}") +except requests.exceptions.JSONDecodeError: + print("Response is not valid JSON") + +# Check for errors +try: + response.raise_for_status() +except requests.exceptions.HTTPError as e: + print(f"HTTP Error: {e}") +``` + +### Streaming Large Responses + +```python +import requests + +# Stream large file download +url = 'https://example.com/large-file.zip' +response = requests.get(url, stream=True) + +with open('large-file.zip', 'wb') as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + +# Stream and process lines +response = requests.get('https://example.com/large-text-file.txt', stream=True) +for line in response.iter_lines(decode_unicode=True): + print(line) + +response.close() # Important to close streamed responses +``` + +### Response Context Manager + +```python +import requests + +# Use response as context manager for automatic cleanup +with requests.get('https://example.com/data.json', stream=True) as response: + response.raise_for_status() + data = response.json() + # Process data... +# Response is automatically closed +``` + +### Advanced Request Preparation + +```python +import requests + +# Create and customize a request +req = requests.Request( + method='POST', + url='https://api.example.com/data', + json={'key': 'value'}, + headers={'User-Agent': 'MyApp/1.0'}, + auth=('user', 'pass') +) + +# Prepare and inspect before sending +prepared = req.prepare() +print(f"Method: {prepared.method}") +print(f"URL: {prepared.url}") +print(f"Headers: {prepared.headers}") +print(f"Body: {prepared.body}") + +# Send the prepared request +with requests.Session() as s: + response = s.send(prepared, timeout=30) +``` + +### Response History and Redirects + +```python +import requests + +response = requests.get('https://github.com') + +# Check redirect history +if response.history: + print("Request was redirected") + for resp in response.history: + print(f"Redirect from: {resp.url} -> {resp.status_code}") + print(f"Final URL: {response.url}") + +# Check redirect type +if response.is_redirect: + print("Response is a redirect") +if response.is_permanent_redirect: + print("Response is a permanent redirect") +``` + +## Constants + +```python { .api } +# Redirect status codes +REDIRECT_STATI: tuple # (301, 302, 303, 307, 308) + +# Default limits +DEFAULT_REDIRECT_LIMIT: int # 30 +CONTENT_CHUNK_SIZE: int # 10 * 1024 +ITER_CHUNK_SIZE: int # 512 +``` + +## Request/Response Lifecycle + +1. **Request Creation**: User creates a Request object with parameters +2. **Request Preparation**: Request is converted to PreparedRequest with exact bytes +3. **Request Sending**: PreparedRequest is sent via adapter (usually HTTPAdapter) +4. **Response Creation**: Server response is converted to Response object +5. **Response Processing**: User accesses response data, headers, status, etc. +6. **Cleanup**: Response connection is returned to pool or closed \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/sessions.md b/.tessl/tiles/tessl/pypi-requests/docs/sessions.md new file mode 100644 index 000000000..8b9f5d87d --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/sessions.md @@ -0,0 +1,259 @@ +# Sessions + +Session objects provide a way to persist certain parameters across requests, enabling connection pooling, cookie persistence, and default configuration. Sessions are essential for maintaining state across multiple requests and improving performance through connection reuse. + +## Capabilities + +### Session Class + +The Session class provides persistent configuration and connection pooling across multiple requests. + +```python { .api } +class Session: + """ + Session object for persisting settings across requests. + + Attributes: + - headers: Default headers for all requests + - cookies: Cookie jar for persistent cookies + - auth: Default authentication + - proxies: Default proxy configuration + - hooks: Event hooks + - params: Default URL parameters + - verify: Default SSL verification setting + - cert: Default client certificate + - max_redirects: Maximum number of redirects to follow + - trust_env: Whether to trust environment variables for configuration + - stream: Default streaming setting + - adapters: OrderedDict of mounted transport adapters + """ + + def __init__(self): + """Initialize a new Session.""" + + def __enter__(self): + """Context manager entry.""" + + def __exit__(self, *args): + """Context manager exit.""" + + def prepare_request(self, request: Request) -> PreparedRequest: + """ + Prepare a Request object for sending. + + Parameters: + - request: Request object to prepare + + Returns: + PreparedRequest object ready to send + """ + + def request(self, method: str, url: str, **kwargs) -> Response: + """ + Send a request using the session configuration. + + Parameters: + - method: HTTP method + - url: URL for the request + - **kwargs: additional request parameters + + Returns: + Response object + """ + + def get(self, url: str, **kwargs) -> Response: + """Send a GET request using session configuration.""" + + def options(self, url: str, **kwargs) -> Response: + """Send an OPTIONS request using session configuration.""" + + def head(self, url: str, **kwargs) -> Response: + """Send a HEAD request using session configuration.""" + + def post(self, url: str, data=None, json=None, **kwargs) -> Response: + """Send a POST request using session configuration.""" + + def put(self, url: str, data=None, **kwargs) -> Response: + """Send a PUT request using session configuration.""" + + def patch(self, url: str, data=None, **kwargs) -> Response: + """Send a PATCH request using session configuration.""" + + def delete(self, url: str, **kwargs) -> Response: + """Send a DELETE request using session configuration.""" + + def send(self, request: PreparedRequest, **kwargs) -> Response: + """ + Send a prepared request. + + Parameters: + - request: PreparedRequest object + - **kwargs: additional sending parameters like timeout, verify, etc. + + Returns: + Response object + """ + + def merge_environment_settings(self, url: str, proxies: dict, stream: bool, + verify: Union[bool, str], cert: Union[str, tuple]): + """ + Merge environment settings with session settings. + + Parameters: + - url: Request URL + - proxies: Proxy configuration + - stream: Stream setting + - verify: SSL verification setting + - cert: Client certificate setting + + Returns: + Dict of merged settings + """ + + def get_adapter(self, url: str): + """ + Get the appropriate adapter for the given URL. + + Parameters: + - url: URL to get adapter for + + Returns: + Adapter instance + """ + + def close(self): + """Close the session and clean up resources.""" + + def mount(self, prefix: str, adapter): + """ + Register an adapter for a URL prefix. + + Parameters: + - prefix: URL prefix (e.g., 'https://') + - adapter: Adapter instance to mount + """ +``` + +### Session Factory Function + +Convenience function to create a new session. + +```python { .api } +def session() -> Session: + """ + Create and return a new Session object. + + Returns: + New Session instance + """ +``` + +## Usage Examples + +### Basic Session Usage + +```python +import requests + +# Create a session +s = requests.Session() + +# Set default headers and authentication +s.headers.update({'User-Agent': 'MyApp/1.0'}) +s.auth = ('username', 'password') + +# Make requests using the session +response1 = s.get('https://api.example.com/data') +response2 = s.post('https://api.example.com/update', json={'key': 'value'}) + +# Cookies are automatically persisted +response3 = s.get('https://api.example.com/profile') # Cookies from previous requests are sent + +# Close the session +s.close() +``` + +### Session as Context Manager + +```python +import requests + +# Use session as a context manager for automatic cleanup +with requests.Session() as s: + s.headers.update({'Authorization': 'Bearer token123'}) + + response = s.get('https://api.example.com/protected') + data = response.json() + + # Process data... + +# Session is automatically closed when exiting the context +``` + +### Persistent Configuration + +```python +import requests + +s = requests.Session() + +# Set persistent configuration +s.headers.update({ + 'User-Agent': 'MyApp/2.0', + 'Accept': 'application/json' +}) +s.auth = ('api_user', 'api_pass') +s.proxies = {'http': 'http://proxy.example.com:8080'} +s.verify = '/path/to/ca-bundle.crt' + +# All requests will use these settings by default +response1 = s.get('https://api.example.com/users') +response2 = s.post('https://api.example.com/data', json={'name': 'John'}) + +# Override settings for specific requests +response3 = s.get('https://other-api.com/data', auth=None, verify=False) +``` + +### Connection Pooling Benefits + +```python +import requests + +# Without session - new connection for each request +for i in range(10): + requests.get('https://api.example.com/data/' + str(i)) # 10 separate connections + +# With session - connection pooling +with requests.Session() as s: + for i in range(10): + s.get('https://api.example.com/data/' + str(i)) # Reuses connections +``` + +## Session Attributes + +Sessions have several configurable attributes: + +- **headers**: `CaseInsensitiveDict` - Default headers for all requests +- **cookies**: `RequestsCookieJar` - Persistent cookie storage +- **auth**: `AuthType` - Default authentication handler +- **proxies**: `Dict[str, str]` - Default proxy configuration +- **hooks**: `Dict[str, List[Callable]]` - Event hooks for request/response processing +- **params**: `Dict[str, str]` - Default URL parameters +- **verify**: `Union[bool, str]` - Default SSL certificate verification +- **cert**: `Union[str, Tuple[str, str]]` - Default client certificate +- **max_redirects**: `int` - Maximum redirects to follow (default: 30) +- **trust_env**: `bool` - Trust environment variables for proxy config +- **stream**: `bool` - Default streaming behavior + +## Session Methods vs Module Functions + +Session methods provide the same interface as module-level functions but with persistent configuration: + +| Module Function | Session Method | Benefit | +|----------------|----------------|---------| +| `requests.get()` | `session.get()` | Persistent headers, cookies, auth | +| `requests.post()` | `session.post()` | Connection pooling | +| `requests.put()` | `session.put()` | Proxy configuration | +| `requests.delete()` | `session.delete()` | SSL settings | + +Session methods inherit all session configuration but can be overridden with method-specific parameters. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/status-codes.md b/.tessl/tiles/tessl/pypi-requests/docs/status-codes.md new file mode 100644 index 000000000..e096abb3a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/status-codes.md @@ -0,0 +1,373 @@ +# Status Codes + +Convenient access to HTTP status codes through named constants and lookup functionality. The requests library provides a `codes` object that maps common status code names to their numerical values. + +## Capabilities + +### Status Code Lookup + +The `codes` object provides multiple ways to access status codes by name. + +```python { .api } +codes: LookupDict +# A lookup dictionary that maps status code names to their numerical values +# Supports both attribute and dictionary-style access +# Multiple names can map to the same status code +# Both upper and lowercase names are supported +``` + +## Usage Examples + +### Basic Status Code Access + +```python +import requests + +# Access status codes by name +print(requests.codes.ok) # 200 +print(requests.codes.not_found) # 404 +print(requests.codes.internal_server_error) # 500 + +# Dictionary-style access +print(requests.codes['ok']) # 200 +print(requests.codes['not_found']) # 404 +print(requests.codes['server_error']) # 500 + +# Case-insensitive access +print(requests.codes.OK) # 200 +print(requests.codes.Ok) # 200 +print(requests.codes.okay) # 200 +``` + +### Status Code Checking + +```python +import requests + +response = requests.get('https://httpbin.org/status/200') + +# Check specific status codes +if response.status_code == requests.codes.ok: + print("Request successful") + +if response.status_code == requests.codes.not_found: + print("Resource not found") + +# Check for success range +if 200 <= response.status_code < 300: + print("Success") + +# Using response.ok property (shorthand for status < 400) +if response.ok: + print("Request successful") +``` + +### Status Code Categories + +```python +import requests + +def categorize_status(status_code): + """Categorize HTTP status codes.""" + if 100 <= status_code < 200: + return "Informational" + elif 200 <= status_code < 300: + return "Success" + elif 300 <= status_code < 400: + return "Redirection" + elif 400 <= status_code < 500: + return "Client Error" + elif 500 <= status_code < 600: + return "Server Error" + else: + return "Unknown" + +response = requests.get('https://httpbin.org/status/404') +category = categorize_status(response.status_code) +print(f"Status {response.status_code} is a {category}") +``` + +### Common Status Code Names + +```python +import requests + +# Informational (1xx) +codes.continue_ # 100 +codes.switching_protocols # 101 +codes.processing # 102 + +# Success (2xx) +codes.ok # 200 +codes.okay # 200 (alias) +codes.all_ok # 200 (alias) +codes.all_good # 200 (alias) +codes['\\o/'] # 200 (fun alias) +codes['✓'] # 200 (checkmark alias) +codes.created # 201 +codes.accepted # 202 +codes.no_content # 204 + +# Redirection (3xx) +codes.multiple_choices # 300 +codes.moved_permanently # 301 +codes.moved # 301 (alias) +codes.found # 302 +codes.see_other # 303 +codes.not_modified # 304 +codes.temporary_redirect # 307 +codes.permanent_redirect # 308 + +# Client Error (4xx) +codes.bad_request # 400 +codes.unauthorized # 401 +codes.forbidden # 403 +codes.not_found # 404 +codes.method_not_allowed # 405 +codes.not_acceptable # 406 +codes.request_timeout # 408 +codes.conflict # 409 +codes.gone # 410 +codes.precondition_failed # 412 +codes.request_entity_too_large # 413 +codes.unsupported_media_type # 415 +codes.too_many_requests # 429 + +# Server Error (5xx) +codes.internal_server_error # 500 +codes.server_error # 500 (alias) +codes.not_implemented # 501 +codes.bad_gateway # 502 +codes.service_unavailable # 503 +codes.gateway_timeout # 504 +``` + +### Response Status Checking with Codes + +```python +import requests + +def handle_response(response): + """Handle response based on status code.""" + + if response.status_code == requests.codes.ok: + return response.json() + + elif response.status_code == requests.codes.created: + print("Resource created successfully") + return response.json() + + elif response.status_code == requests.codes.no_content: + print("Operation successful, no content returned") + return None + + elif response.status_code == requests.codes.not_modified: + print("Resource not modified, using cached version") + return None + + elif response.status_code == requests.codes.bad_request: + print("Bad request - check your parameters") + return None + + elif response.status_code == requests.codes.unauthorized: + print("Authentication required") + return None + + elif response.status_code == requests.codes.forbidden: + print("Access forbidden") + return None + + elif response.status_code == requests.codes.not_found: + print("Resource not found") + return None + + elif response.status_code == requests.codes.too_many_requests: + print("Rate limit exceeded") + return None + + elif response.status_code == requests.codes.internal_server_error: + print("Internal server error") + return None + + elif response.status_code == requests.codes.service_unavailable: + print("Service temporarily unavailable") + return None + + else: + print(f"Unexpected status code: {response.status_code}") + return None + +# Usage +response = requests.get('https://api.example.com/data') +data = handle_response(response) +``` + +### Status Code Comparison + +```python +import requests + +response = requests.get('https://httpbin.org/status/200') + +# Multiple ways to check for success +if response.status_code == 200: + print("Success (numeric)") + +if response.status_code == requests.codes.ok: + print("Success (using codes)") + +if response.ok: + print("Success (using ok property)") + +# Check for specific error conditions +if response.status_code in [requests.codes.unauthorized, requests.codes.forbidden]: + print("Authentication or authorization error") + +# Check ranges +if 400 <= response.status_code < 500: + print("Client error") +elif 500 <= response.status_code < 600: + print("Server error") +``` + +### Custom Status Code Handling + +```python +import requests + +class StatusHandler: + """Custom status code handler.""" + + @staticmethod + def is_success(status_code): + """Check if status code indicates success.""" + return 200 <= status_code < 300 + + @staticmethod + def is_redirect(status_code): + """Check if status code indicates redirect.""" + return status_code in [ + requests.codes.moved_permanently, + requests.codes.found, + requests.codes.see_other, + requests.codes.temporary_redirect, + requests.codes.permanent_redirect + ] + + @staticmethod + def is_client_error(status_code): + """Check if status code indicates client error.""" + return 400 <= status_code < 500 + + @staticmethod + def is_server_error(status_code): + """Check if status code indicates server error.""" + return 500 <= status_code < 600 + + @staticmethod + def should_retry(status_code): + """Determine if request should be retried based on status.""" + retry_codes = [ + requests.codes.request_timeout, + requests.codes.too_many_requests, + requests.codes.internal_server_error, + requests.codes.bad_gateway, + requests.codes.service_unavailable, + requests.codes.gateway_timeout + ] + return status_code in retry_codes + +# Usage +response = requests.get('https://api.example.com/data') +handler = StatusHandler() + +if handler.is_success(response.status_code): + data = response.json() +elif handler.should_retry(response.status_code): + print("Should retry this request") +elif handler.is_client_error(response.status_code): + print("Client error - check request") +else: + print("Unexpected status code") +``` + +### Status Code Logging + +```python +import requests +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +def logged_request(url): + """Make request with detailed status logging.""" + try: + response = requests.get(url) + + # Log status information + if response.status_code == requests.codes.ok: + logger.info(f"Request successful: {response.status_code}") + elif 300 <= response.status_code < 400: + logger.info(f"Redirect: {response.status_code} -> {response.headers.get('Location', 'Unknown')}") + elif 400 <= response.status_code < 500: + logger.warning(f"Client error: {response.status_code} - {response.reason}") + elif 500 <= response.status_code < 600: + logger.error(f"Server error: {response.status_code} - {response.reason}") + else: + logger.info(f"Unexpected status: {response.status_code}") + + return response + + except requests.exceptions.RequestException as e: + logger.error(f"Request failed: {e}") + raise + +# Usage +response = logged_request('https://httpbin.org/status/404') +``` + +## Complete Status Code Reference + +The `codes` object includes mappings for all standard HTTP status codes with multiple name variations: + +### Informational (1xx) +- `continue` (100) +- `switching_protocols` (101) +- `processing` (102) + +### Success (2xx) +- `ok`, `okay`, `all_ok`, `all_good`, `\\o/`, `✓` (200) +- `created` (201) +- `accepted` (202) +- `no_content` (204) +- `partial_content` (206) + +### Redirection (3xx) +- `moved_permanently`, `moved` (301) +- `found` (302) +- `see_other`, `other` (303) +- `not_modified` (304) +- `temporary_redirect` (307) +- `permanent_redirect` (308) + +### Client Error (4xx) +- `bad_request` (400) +- `unauthorized` (401) +- `forbidden` (403) +- `not_found` (404) +- `method_not_allowed` (405) +- `request_timeout` (408) +- `conflict` (409) +- `gone` (410) +- `too_many_requests` (429) + +### Server Error (5xx) +- `internal_server_error`, `server_error` (500) +- `not_implemented` (501) +- `bad_gateway` (502) +- `service_unavailable` (503) +- `gateway_timeout` (504) + +Many codes have multiple aliases - both the formal name and common variations are supported. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/structures.md b/.tessl/tiles/tessl/pypi-requests/docs/structures.md new file mode 100644 index 000000000..b8afbac84 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/docs/structures.md @@ -0,0 +1,457 @@ +# Data Structures + +Data structure classes that provide enhanced dictionary interfaces with special behaviors for HTTP-related operations. These structures are used throughout the requests library for headers, status codes, and general data handling. + +## Capabilities + +### CaseInsensitiveDict Class + +Dictionary with case-insensitive key lookup, primarily used for HTTP headers. + +```python { .api } +class CaseInsensitiveDict(dict): + """ + A case-insensitive dictionary implementation. + + Used for HTTP headers where keys should be treated case-insensitively + according to HTTP specification. + """ + + def __init__(self, data=None, **kwargs): + """ + Initialize CaseInsensitiveDict. + + Parameters: + - data: Initial data (dict, list of tuples, or another CaseInsensitiveDict) + - **kwargs: Additional key-value pairs + """ + + def __setitem__(self, key, value): + """Set value with case-insensitive key.""" + + def __getitem__(self, key): + """Get value with case-insensitive key lookup.""" + + def __delitem__(self, key): + """Delete item with case-insensitive key lookup.""" + + def __iter__(self): + """Iterate over original-case keys.""" + + def __len__(self) -> int: + """Get number of items.""" + + def __eq__(self, other) -> bool: + """Compare with another mapping (case-insensitive).""" + + def __repr__(self) -> str: + """String representation.""" + + def lower_items(self): + """ + Iterate over (lowercase_key, value) pairs. + + Yields: + Tuples of (lowercase_key, value) + """ + + def copy(self) -> 'CaseInsensitiveDict': + """Create a copy of the dictionary.""" +``` + +### LookupDict Class + +Dictionary subclass with attribute-style access, used for status codes. + +```python { .api } +class LookupDict(dict): + """ + A dictionary that supports both item and attribute access. + + Used for status codes to enable both codes['ok'] and codes.ok syntax. + """ + + def __init__(self, name=None): + """ + Initialize LookupDict. + + Parameters: + - name: Optional name for the lookup dict + """ + + def __getitem__(self, key): + """Get item with fallback to __dict__ lookup.""" + + def get(self, key, default=None): + """ + Get value with default. + + Parameters: + - key: Key to look up + - default: Default value if key not found + + Returns: + Value or default + """ + + def __repr__(self) -> str: + """String representation.""" +``` + +## Usage Examples + +### CaseInsensitiveDict Usage + +```python +import requests +from requests.structures import CaseInsensitiveDict + +# CaseInsensitiveDict is used for response headers +response = requests.get('https://httpbin.org/get') +headers = response.headers + +# All these access the same header (case-insensitive) +print(headers['Content-Type']) # Works +print(headers['content-type']) # Works +print(headers['CONTENT-TYPE']) # Works +print(headers['Content-type']) # Works + +# Check header existence (case-insensitive) +print('content-type' in headers) # True +print('Content-Type' in headers) # True + +# Iterate over headers (preserves original case) +for name, value in headers.items(): + print(f"{name}: {value}") +``` + +### Manual CaseInsensitiveDict Creation + +```python +from requests.structures import CaseInsensitiveDict + +# Create from dict +headers = CaseInsensitiveDict({ + 'Content-Type': 'application/json', + 'Authorization': 'Bearer token123', + 'User-Agent': 'MyApp/1.0' +}) + +# Access with any case +print(headers['content-type']) # 'application/json' +print(headers['AUTHORIZATION']) # 'Bearer token123' +print(headers['user-agent']) # 'MyApp/1.0' + +# Set with any case +headers['accept'] = 'application/json' +headers['CACHE-CONTROL'] = 'no-cache' + +# Check existence (case-insensitive) +print('Accept' in headers) # True +print('cache-control' in headers) # True + +# Delete with any case +del headers['USER-AGENT'] +print('user-agent' in headers) # False +``` + +### CaseInsensitiveDict Operations + +```python +from requests.structures import CaseInsensitiveDict + +# Create from various sources +headers1 = CaseInsensitiveDict([ + ('Content-Type', 'application/json'), + ('Accept', 'application/json') +]) + +headers2 = CaseInsensitiveDict( + authorization='Bearer token', + user_agent='MyApp/1.0' +) + +# Copy dictionary +headers_copy = headers1.copy() +headers_copy['X-Custom'] = 'custom-value' + +# Compare dictionaries (case-insensitive) +h1 = CaseInsensitiveDict({'Content-Type': 'application/json'}) +h2 = CaseInsensitiveDict({'content-type': 'application/json'}) +print(h1 == h2) # True (case-insensitive comparison) + +# Get lowercase items for processing +headers = CaseInsensitiveDict({ + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Authorization': 'Bearer token' +}) + +for lower_key, value in headers.lower_items(): + print(f"{lower_key}: {value}") +# content-type: application/json +# accept: application/json +# authorization: Bearer token +``` + +### LookupDict Usage + +```python +import requests + +# LookupDict is used for status codes +codes = requests.codes + +# Attribute access +print(codes.ok) # 200 +print(codes.not_found) # 404 +print(codes.internal_server_error) # 500 + +# Dictionary access +print(codes['ok']) # 200 +print(codes['not_found']) # 404 +print(codes['server_error']) # 500 + +# Both work the same way +status_code = 404 +if status_code == codes.not_found: + print("Resource not found") + +if status_code == codes['not_found']: + print("Resource not found") +``` + +### Custom LookupDict + +```python +from requests.structures import LookupDict + +# Create custom lookup dict +http_methods = LookupDict('HTTP Methods') +http_methods.update({ + 'get': 'GET', + 'post': 'POST', + 'put': 'PUT', + 'delete': 'DELETE', + 'head': 'HEAD', + 'options': 'OPTIONS', + 'patch': 'PATCH' +}) + +# Use both access methods +print(http_methods.get) # 'GET' +print(http_methods['post']) # 'POST' +print(http_methods.delete) # 'DELETE' + +# Get with default +print(http_methods.get('trace', 'TRACE')) # 'TRACE' +print(http_methods.get('connect')) # None +``` + +### Practical Header Management + +```python +import requests +from requests.structures import CaseInsensitiveDict + +def build_headers(content_type=None, auth_token=None, custom_headers=None): + """Build headers with case-insensitive handling.""" + headers = CaseInsensitiveDict() + + # Set standard headers + headers['User-Agent'] = 'MyApp/2.0' + + if content_type: + headers['Content-Type'] = content_type + + if auth_token: + headers['Authorization'] = f'Bearer {auth_token}' + + # Merge custom headers (case-insensitive) + if custom_headers: + for key, value in custom_headers.items(): + headers[key] = value + + return headers + +# Build headers +headers = build_headers( + content_type='application/json', + auth_token='abc123', + custom_headers={ + 'x-api-version': '2.0', + 'X-CUSTOM-HEADER': 'custom-value' + } +) + +# Use with request +response = requests.post('https://httpbin.org/post', + headers=headers, + json={'key': 'value'}) + +# Examine sent headers (case preserved) +sent_headers = response.request.headers +for name, value in sent_headers.items(): + print(f"{name}: {value}") +``` + +### Header Manipulation + +```python +import requests +from requests.structures import CaseInsensitiveDict + +# Start with response headers +response = requests.get('https://httpbin.org/response-headers?foo=bar') +headers = response.headers + +print(f"Original headers: {len(headers)} items") + +# Modify headers (case-insensitive) +modified_headers = headers.copy() +modified_headers['cache-control'] = 'no-cache' # Might overwrite Cache-Control +modified_headers['X-Custom'] = 'custom-value' # Add new header +del modified_headers['date'] # Remove Date header + +# Use modified headers in new request +new_response = requests.get('https://httpbin.org/headers', + headers=modified_headers) + +# Check what was sent +request_headers = new_response.json()['headers'] +for name, value in request_headers.items(): + print(f"{name}: {value}") +``` + +### Session Header Management + +```python +import requests +from requests.structures import CaseInsensitiveDict + +# Session headers are CaseInsensitiveDict +session = requests.Session() +print(type(session.headers)) # + +# Set session headers (case-insensitive) +session.headers.update({ + 'User-Agent': 'MyApp/1.0', + 'accept': 'application/json', + 'AUTHORIZATION': 'Bearer token123' +}) + +# Check headers with different cases +print('user-agent' in session.headers) # True +print(session.headers['Accept']) # 'application/json' +print(session.headers['authorization']) # 'Bearer token123' + +# Headers persist across requests +response1 = session.get('https://httpbin.org/headers') +response2 = session.post('https://httpbin.org/post', json={'data': 'test'}) + +# Both requests include the session headers +print("Request 1 headers:", response1.json()['headers']) +print("Request 2 headers:", response2.json()['headers']) +``` + +### Status Code Management + +```python +import requests +from requests.structures import LookupDict + +# The codes object is a LookupDict +print(type(requests.codes)) # + +# Create custom status mappings +api_codes = LookupDict('API Status Codes') +api_codes.update({ + 'success': 200, + 'created': 201, + 'accepted': 202, + 'bad_request': 400, + 'unauthorized': 401, + 'forbidden': 403, + 'not_found': 404, + 'server_error': 500 +}) + +# Use in response handling +def handle_api_response(response): + status = response.status_code + + if status == api_codes.success: + return response.json() + elif status == api_codes.created: + print("Resource created") + return response.json() + elif status == api_codes.not_found: + print("Resource not found") + return None + elif status == api_codes.server_error: + print("Server error occurred") + return None + else: + print(f"Unexpected status: {status}") + return None + +# Test with different responses +responses = [ + requests.get('https://httpbin.org/status/200'), + requests.get('https://httpbin.org/status/404'), + requests.get('https://httpbin.org/status/500') +] + +for resp in responses: + result = handle_api_response(resp) + print(f"Status {resp.status_code}: {result}") +``` + +## Structure Comparison + +### Standard Dict vs CaseInsensitiveDict + +```python +from requests.structures import CaseInsensitiveDict + +# Standard dict - case-sensitive +standard = {'Content-Type': 'application/json'} +print('content-type' in standard) # False +print('Content-Type' in standard) # True + +# CaseInsensitiveDict - case-insensitive +case_insensitive = CaseInsensitiveDict({'Content-Type': 'application/json'}) +print('content-type' in case_insensitive) # True +print('Content-Type' in case_insensitive) # True +print('CONTENT-TYPE' in case_insensitive) # True + +# Key preservation +print(list(standard.keys())) # ['Content-Type'] +print(list(case_insensitive.keys())) # ['Content-Type'] (original case preserved) + +# Lowercase iteration +for key, value in case_insensitive.lower_items(): + print(f"Lowercase: {key} -> {value}") # content-type -> application/json +``` + +### Standard Dict vs LookupDict + +```python +from requests.structures import LookupDict + +# Standard dict - item access only +standard = {'ok': 200, 'not_found': 404} +print(standard['ok']) # 200 +# print(standard.ok) # AttributeError + +# LookupDict - both item and attribute access +lookup = LookupDict() +lookup.update({'ok': 200, 'not_found': 404}) +print(lookup['ok']) # 200 +print(lookup.ok) # 200 (attribute access) +print(lookup.not_found) # 404 + +# Graceful handling of missing keys +print(lookup.get('missing')) # None +print(lookup.get('missing', 'default')) # 'default' +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/tile.json b/.tessl/tiles/tessl/pypi-requests/tile.json new file mode 100644 index 000000000..8bdce0a67 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-requests/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-requests", + "version": "2.32.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/requests@2.32.4", + "summary": "Python HTTP library for making HTTP requests with elegant API for humans." +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/console.md b/.tessl/tiles/tessl/pypi-rich/docs/console.md new file mode 100644 index 000000000..a15f3e4c7 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/console.md @@ -0,0 +1,494 @@ +# Console and Output + +Core console functionality for printing styled text, handling terminal capabilities, and managing output formatting. The Console class is the central component of Rich, providing comprehensive terminal output management with automatic capability detection. + +## Capabilities + +### Console Class + +The main Console class handles all terminal output with support for styling, color, markup, and advanced formatting features. + +```python { .api } +class Console: + """ + A high level console interface for Rich. + + Args: + color_system: The color system to use ("auto", "standard", "256", "truecolor", "windows", None) + force_terminal: Force terminal detection + force_jupyter: Force Jupyter mode detection + force_interactive: Force interactive mode + soft_wrap: Enable soft wrapping of long lines + theme: Default theme or None for builtin theme + stderr: Write to stderr instead of stdout + file: File object to write to or None for stdout + quiet: Suppress all output if True + width: Width of output or None to auto-detect + height: Height of output or None to auto-detect + style: Default style for console output + no_color: Disable color output + tab_size: Size of tabs in characters + record: Record all output for later playback + markup: Enable Rich markup processing + emoji: Enable emoji code rendering + emoji_variant: Emoji variant preference + highlight: Enable highlighting of output + log_time: Include timestamp in log output + log_path: Include path in log output + log_time_format: Format for log timestamps + highlighter: Default highlighter for output + legacy_windows: Enable legacy Windows terminal support + safe_box: Disable box characters incompatible with legacy terminals + get_datetime: Callable to get current datetime + get_time: Callable to get current time + """ + def __init__( + self, + color_system: Optional[str] = "auto", + force_terminal: Optional[bool] = None, + force_jupyter: Optional[bool] = None, + force_interactive: Optional[bool] = None, + soft_wrap: bool = False, + theme: Optional[Theme] = None, + stderr: bool = False, + file: Optional[TextIO] = None, + quiet: bool = False, + width: Optional[int] = None, + height: Optional[int] = None, + style: Optional[StyleType] = None, + no_color: Optional[bool] = None, + tab_size: int = 8, + record: bool = False, + markup: bool = True, + emoji: bool = True, + emoji_variant: Optional[EmojiVariant] = None, + highlight: bool = True, + log_time: bool = True, + log_path: bool = True, + log_time_format: Union[str, FormatTimeCallable] = "[%X]", + highlighter: Optional[HighlighterType] = ReprHighlighter(), + legacy_windows: Optional[bool] = None, + safe_box: bool = True, + get_datetime: Optional[Callable[[], datetime]] = None, + get_time: Optional[Callable[[], float]] = None, + _environ: Optional[Mapping[str, str]] = None, + ): ... + + def print( + self, + *objects: Any, + sep: str = " ", + end: str = "\n", + style: Optional[StyleType] = None, + justify: Optional[JustifyMethod] = None, + overflow: Optional[OverflowMethod] = None, + no_wrap: Optional[bool] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + width: Optional[int] = None, + height: Optional[int] = None, + crop: bool = True, + soft_wrap: Optional[bool] = None, + new_line_start: bool = False, + ) -> None: + """ + Print to the console with rich formatting. + + Args: + *objects: Objects to print + sep: Separator between objects + end: String to print at end + style: Style to apply to output + justify: Text justification method + overflow: Overflow handling method + no_wrap: Disable text wrapping + emoji: Enable emoji rendering + markup: Enable markup processing + highlight: Enable highlighting + width: Maximum width of output + height: Maximum height of output + crop: Crop output to fit console + soft_wrap: Enable soft wrapping + new_line_start: Start on new line + """ + + def log( + self, + *objects: Any, + sep: str = " ", + end: str = "\n", + style: Optional[StyleType] = None, + justify: Optional[JustifyMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + log_locals: bool = False, + _stack_offset: int = 1, + ) -> None: + """ + Print with timestamp and caller information. + + Args: + *objects: Objects to log + sep: Separator between objects + end: String to print at end + style: Style to apply to output + justify: Text justification method + emoji: Enable emoji rendering + markup: Enable markup processing + highlight: Enable highlighting + log_locals: Include local variables in output + """ + + def clear(self, home: bool = True) -> None: + """ + Clear the console. + + Args: + home: Move cursor to home position + """ + + def rule( + self, + title: TextType = "", + *, + characters: str = "─", + style: StyleType = "rule.line", + end: str = "\n", + align: AlignMethod = "center", + ) -> None: + """ + Print a horizontal rule with optional title. + + Args: + title: Title text for the rule + characters: Characters to use for the rule + style: Style for the rule + end: String to print at end + align: Alignment of title + """ + + def status( + self, + status: RenderableType, + *, + spinner: str = "dots", + spinner_style: StyleType = "status.spinner", + speed: float = 1.0, + ) -> "Status": + """ + Create a status context manager with spinner. + + Args: + status: Status text or renderable + spinner: Spinner style name + spinner_style: Style for spinner + speed: Spinner animation speed + + Returns: + Status context manager + """ + + def capture(self) -> "Capture": + """ + Create a capture context to record console output. + + Returns: + Capture context manager + """ + + def pager( + self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False + ) -> "PagerContext": + """ + Create a pager context for scrollable output. + + Args: + pager: Pager instance or None for system pager + styles: Include styles in paged output + links: Include links in paged output + + Returns: + Pager context manager + """ + + def screen( + self, *, hide_cursor: bool = True, alt_screen: bool = True + ) -> "ScreenContext": + """ + Create a screen context for full-screen applications. + + Args: + hide_cursor: Hide the cursor + alt_screen: Use alternate screen buffer + + Returns: + Screen context manager + """ + + def measure(self, renderable: RenderableType) -> Measurement: + """ + Measure the dimensions of a renderable. + + Args: + renderable: Object to measure + + Returns: + Measurement with minimum and maximum dimensions + """ + + def render( + self, renderable: RenderableType, options: Optional[ConsoleOptions] = None + ) -> Iterable[Segment]: + """ + Render a renderable to segments. + + Args: + renderable: Object to render + options: Console options or None for default + + Returns: + Iterator of segments + """ + + def export_html( + self, + *, + theme: Optional[TerminalTheme] = None, + clear: bool = True, + code_format: Optional[str] = None, + inline_styles: bool = False, + ) -> str: + """ + Export console content as HTML. + + Args: + theme: Terminal theme or None for default + clear: Clear console after export + code_format: Code format template + inline_styles: Use inline CSS styles + + Returns: + HTML string + """ + + def export_svg( + self, + *, + title: str = "Rich", + theme: Optional[TerminalTheme] = None, + clear: bool = True, + code_format: str = CONSOLE_SVG_FORMAT, + font_size: float = 14, + unique_id: Optional[str] = None, + ) -> str: + """ + Export console content as SVG. + + Args: + title: SVG title + theme: Terminal theme or None for default + clear: Clear console after export + code_format: SVG format template + font_size: Font size in pixels + unique_id: Unique identifier for SVG elements + + Returns: + SVG string + """ + + def export_text(self, *, clear: bool = True, styles: bool = False) -> str: + """ + Export console content as plain text. + + Args: + clear: Clear console after export + styles: Include ANSI styles in output + + Returns: + Plain text string + """ + + # Properties + @property + def size(self) -> ConsoleDimensions: + """Get console dimensions (width, height).""" + + @property + def width(self) -> int: + """Get console width in characters.""" + + @property + def height(self) -> int: + """Get console height in lines.""" + + @property + def is_terminal(self) -> bool: + """Check if output is a terminal.""" + + @property + def is_dumb_terminal(self) -> bool: + """Check if terminal has limited capabilities.""" + + @property + def is_interactive(self) -> bool: + """Check if console is interactive.""" + + @property + def color_system(self) -> Optional[str]: + """Get the active color system.""" + + @property + def encoding(self) -> str: + """Get the output encoding.""" + + @property + def is_jupyter(self) -> bool: + """Check if running in Jupyter.""" + + @property + def options(self) -> ConsoleOptions: + """Get default console options.""" +``` + +### Global Functions + +Module-level convenience functions for common operations. + +```python { .api } +def get_console() -> Console: + """ + Get a global Console instance. + + Returns: + Global console instance + """ + +def reconfigure(*args: Any, **kwargs: Any) -> None: + """ + Reconfigure the global console. + + Args: + *args: Positional arguments for Console constructor + **kwargs: Keyword arguments for Console constructor + """ + +def print( + *objects: Any, + sep: str = " ", + end: str = "\n", + file: Optional[IO[str]] = None, + flush: bool = False, +) -> None: + """ + Rich-enhanced print function with markup support. + + Args: + *objects: Objects to print + sep: Separator between objects + end: String to print at end + file: File to write to or None for stdout + flush: Force flush output (ignored, Rich always flushes) + """ + +def print_json( + json: Optional[str] = None, + *, + data: Any = None, + indent: Union[None, int, str] = 2, + highlight: bool = True, + skip_keys: bool = False, + ensure_ascii: bool = False, + check_circular: bool = True, + allow_nan: bool = True, + default: Optional[Callable[[Any], Any]] = None, + sort_keys: bool = False, +) -> None: + """ + Pretty print JSON with syntax highlighting. + + Args: + json: JSON string to print + data: Python data to encode as JSON + indent: Number of spaces to indent + highlight: Enable syntax highlighting + skip_keys: Skip keys that are not basic types + ensure_ascii: Escape non-ASCII characters + check_circular: Check for circular references + allow_nan: Allow NaN and Infinity values + default: Function to handle non-serializable objects + sort_keys: Sort object keys + """ + +def inspect( + obj: Any, + *, + console: Optional[Console] = None, + title: Optional[str] = None, + help: bool = False, + methods: bool = False, + docs: bool = True, + private: bool = False, + dunder: bool = False, + sort: bool = True, + all: bool = False, + value: bool = True, +) -> None: + """ + Inspect any Python object with rich formatting. + + Args: + obj: Object to inspect + console: Console instance or None for global console + title: Title for the inspection or None to use object type + help: Show full help text instead of summary + methods: Include callable methods + docs: Show docstrings + private: Show private attributes (starting with _) + dunder: Show dunder attributes (starting with __) + sort: Sort attributes alphabetically + all: Show all attributes (equivalent to private=True, dunder=True) + value: Show attribute values + """ +``` + +**Usage Examples:** + +```python +from rich.console import Console +from rich import print + +# Basic console usage +console = Console() +console.print("Hello, World!", style="bold red") + +# Enhanced print with markup +print("[bold blue]Information:[/bold blue] Process completed successfully") +print("[red]Error:[/red] Unable to connect to server") + +# Logging with timestamps +console.log("Application started") +console.log("Processing data...", style="yellow") + +# Status spinner +with console.status("Loading data...") as status: + time.sleep(2) + status.update("Processing...") + time.sleep(2) + +# Measuring content +from rich.text import Text +text = Text("Hello World", style="bold") +measurement = console.measure(text) +print(f"Text width: {measurement.minimum}-{measurement.maximum}") + +# Exporting output +console.print("This will be exported", style="green") +html_output = console.export_html() +svg_output = console.export_svg() + +# Object inspection +inspect([1, 2, 3, 4, 5]) +inspect(console, methods=True) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/containers.md b/.tessl/tiles/tessl/pypi-rich/docs/containers.md new file mode 100644 index 000000000..57182e97f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/containers.md @@ -0,0 +1,120 @@ +# Panels and Containers + +Panels, rules, columns, and other container components for organizing content. Rich provides various container components for structuring and organizing terminal output. + +## Capabilities + +### Panel Class + +Decorative panels with borders and titles. + +```python { .api } +class Panel: + """ + Panel with border and optional title. + + Args: + renderable: Content to display in panel + box: Box style for borders + safe_box: Use safe box characters + expand: Expand to fit available width + style: Panel style + border_style: Border style + width: Fixed width or None for auto + height: Fixed height or None for auto + padding: Internal padding + highlight: Enable content highlighting + title: Panel title + title_align: Title alignment + subtitle: Panel subtitle + subtitle_align: Subtitle alignment + """ + def __init__( + self, + renderable: RenderableType, + box: Box = box.ROUNDED, + *, + safe_box: Optional[bool] = None, + expand: bool = True, + style: StyleType = "none", + border_style: StyleType = "none", + width: Optional[int] = None, + height: Optional[int] = None, + padding: PaddingDimensions = (0, 1), + highlight: bool = False, + title: Optional[TextType] = None, + title_align: AlignMethod = "center", + subtitle: Optional[TextType] = None, + subtitle_align: AlignMethod = "center", + ): ... + + @classmethod + def fit( + cls, + renderable: RenderableType, + box: Box = box.ROUNDED, + *, + safe_box: Optional[bool] = None, + style: StyleType = "none", + border_style: StyleType = "none", + padding: PaddingDimensions = (0, 1), + title: Optional[TextType] = None, + title_align: AlignMethod = "center", + ) -> "Panel": + """Create a panel that fits content exactly.""" + +class Rule: + """Horizontal rule with optional title.""" + def __init__( + self, + title: TextType = "", + *, + characters: str = "─", + style: StyleType = "rule.line", + end: str = "\n", + align: AlignMethod = "center", + ): ... + +class Columns: + """Multi-column layout for renderables.""" + def __init__( + self, + renderables: Optional[Iterable[RenderableType]] = None, + width: Optional[int] = None, + padding: PaddingDimensions = (0, 1), + expand: bool = False, + equal: bool = False, + column_first: bool = False, + right_to_left: bool = False, + align: AlignMethod = "left", + title: Optional[TextType] = None, + ): ... +``` + +**Usage Examples:** + +```python +from rich.console import Console +from rich.panel import Panel +from rich.rule import Rule +from rich.columns import Columns + +console = Console() + +# Basic panel +panel = Panel("Hello, World!", title="Greeting") +console.print(panel) + +# Rule +console.print(Rule("Section 1")) +console.print("Content here") +console.print(Rule()) + +# Columns +columns = Columns([ + Panel("Column 1"), + Panel("Column 2"), + Panel("Column 3") +]) +console.print(columns) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/index.md b/.tessl/tiles/tessl/pypi-rich/docs/index.md new file mode 100644 index 000000000..57d36526f --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/index.md @@ -0,0 +1,592 @@ +# Rich + +Rich is a comprehensive Python terminal formatting library that enables developers to create beautiful, interactive command-line interfaces with advanced text styling, color support, and rich output components. It provides a powerful API for rendering styled text with support for RGB/HEX colors, markup-style formatting, and automatic terminal capability detection. + +## Package Information + +- **Package Name**: rich +- **Package Type**: pypi +- **Language**: Python +- **Installation**: `pip install rich` +- **Minimum Python**: 3.8+ +- **Dependencies**: pygments, markdown-it-py + +## Core Imports + +```python +import rich +from rich.console import Console +from rich.text import Text +from rich import print +``` + +Common imports for specific functionality: + +```python +from rich.table import Table +from rich.progress import Progress +from rich.panel import Panel +from rich.syntax import Syntax +from rich.markdown import Markdown +from rich.prompt import Prompt +from rich.live import Live +from rich.layout import Layout +``` + +## Basic Usage + +```python +from rich.console import Console +from rich.text import Text +from rich import print + +# Enhanced print function with markup support +print("Hello [bold magenta]World[/bold magenta]!") +print("[bold red]Alert![/bold red] This is [italic]important[/italic].") + +# Console for advanced features +console = Console() +console.print("Hello", "World!", style="bold blue") + +# Rich text objects +text = Text("Hello World!") +text.stylize("bold magenta", 0, 6) +console.print(text) + +# Simple table +from rich.table import Table +table = Table() +table.add_column("Name") +table.add_column("Score") +table.add_row("Alice", "95") +table.add_row("Bob", "87") +console.print(table) +``` + +## Architecture + +Rich is built around several key components: + +- **Console**: Central output manager handling terminal detection, rendering, and export +- **Text Objects**: Styled text with spans for precise formatting control +- **Renderables**: Protocol for objects that can be rendered to the terminal +- **Styles**: Comprehensive styling system with color, typography, and effects +- **Layout System**: Advanced positioning and sizing for complex UIs +- **Live Display**: Real-time updating content for progress bars and dynamic interfaces + +## Capabilities + +### Console Output and Styling + +Core console functionality for printing styled text, handling terminal capabilities, and managing output formatting. + +```python { .api } +class Console: + def __init__( + self, + color_system: Optional[str] = "auto", + force_terminal: Optional[bool] = None, + force_jupyter: Optional[bool] = None, + force_interactive: Optional[bool] = None, + soft_wrap: bool = False, + theme: Optional[Theme] = None, + stderr: bool = False, + file: Optional[TextIO] = None, + quiet: bool = False, + width: Optional[int] = None, + height: Optional[int] = None, + style: Optional[StyleType] = None, + no_color: Optional[bool] = None, + tab_size: int = 8, + record: bool = False, + markup: bool = True, + emoji: bool = True, + emoji_variant: Optional[EmojiVariant] = None, + highlight: bool = True, + log_time: bool = True, + log_path: bool = True, + log_time_format: Union[str, FormatTimeCallable] = "[%X]", + highlighter: Optional[HighlighterType] = ReprHighlighter(), + legacy_windows: Optional[bool] = None, + safe_box: bool = True, + get_datetime: Optional[Callable[[], datetime]] = None, + get_time: Optional[Callable[[], float]] = None, + _environ: Optional[Mapping[str, str]] = None, + ): ... + + def print( + self, + *objects: Any, + sep: str = " ", + end: str = "\n", + style: Optional[StyleType] = None, + justify: Optional[JustifyMethod] = None, + overflow: Optional[OverflowMethod] = None, + no_wrap: Optional[bool] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + width: Optional[int] = None, + height: Optional[int] = None, + crop: bool = True, + soft_wrap: Optional[bool] = None, + new_line_start: bool = False, + ) -> None: ... + +def print( + *objects: Any, + sep: str = " ", + end: str = "\n", + file: Optional[IO[str]] = None, + flush: bool = False, +) -> None: ... + +def print_json( + json: Optional[str] = None, + *, + data: Any = None, + indent: Union[None, int, str] = 2, + highlight: bool = True, + skip_keys: bool = False, + ensure_ascii: bool = False, + check_circular: bool = True, + allow_nan: bool = True, + default: Optional[Callable[[Any], Any]] = None, + sort_keys: bool = False, +) -> None: ... +``` + +[Console and Output](./console.md) + +### Rich Text and Styling + +Advanced text objects with precise styling control, markup support, and comprehensive formatting options. + +```python { .api } +class Text: + def __init__( + self, + text: str = "", + style: StyleType = "", + *, + justify: Optional[JustifyMethod] = None, + overflow: OverflowMethod = "fold", + no_wrap: Optional[bool] = None, + end: str = "\n", + tab_size: Optional[int] = 8, + spans: Optional[List[Span]] = None, + ): ... + + def stylize( + self, + style: StyleType, + start: int = 0, + end: Optional[int] = None, + ) -> None: ... + + @classmethod + def from_markup( + cls, + text: str, + *, + style: StyleType = "", + emoji: bool = True, + emoji_variant: Optional[EmojiVariant] = None, + ) -> "Text": ... + +class Style: + def __init__( + self, + *, + color: Optional[ColorType] = None, + bgcolor: Optional[ColorType] = None, + bold: Optional[bool] = None, + dim: Optional[bool] = None, + italic: Optional[bool] = None, + underline: Optional[bool] = None, + blink: Optional[bool] = None, + blink2: Optional[bool] = None, + reverse: Optional[bool] = None, + conceal: Optional[bool] = None, + strike: Optional[bool] = None, + underline2: Optional[bool] = None, + frame: Optional[bool] = None, + encircle: Optional[bool] = None, + overline: Optional[bool] = None, + link: Optional[str] = None, + meta: Optional[Dict[str, Any]] = None, + ): ... +``` + +[Text and Styling](./text-styling.md) + +### Tables and Data Display + +Comprehensive table system with flexible columns, styling, borders, and automatic sizing. + +```python { .api } +class Table: + def __init__( + self, + *columns: Union[Column, str], + title: Optional[TextType] = None, + caption: Optional[TextType] = None, + width: Optional[int] = None, + min_width: Optional[int] = None, + box: Optional[Box] = box.HEAVY_HEAD, + safe_box: Optional[bool] = None, + padding: PaddingDimensions = (0, 1), + collapse_padding: bool = False, + pad_edge: bool = True, + expand: bool = False, + show_header: bool = True, + show_footer: bool = False, + show_edge: bool = True, + show_lines: bool = False, + leading: int = 0, + style: StyleType = "", + row_styles: Optional[Iterable[StyleType]] = None, + header_style: Optional[StyleType] = None, + footer_style: Optional[StyleType] = None, + border_style: Optional[StyleType] = None, + title_style: Optional[StyleType] = None, + caption_style: Optional[StyleType] = None, + title_justify: JustifyMethod = "center", + caption_justify: JustifyMethod = "center", + highlight: bool = False, + ): ... + + def add_column( + self, + header: RenderableType = "", + *, + footer: RenderableType = "", + header_style: Optional[StyleType] = None, + footer_style: Optional[StyleType] = None, + style: Optional[StyleType] = None, + justify: JustifyMethod = "left", + vertical: VerticalAlignMethod = "top", + overflow: OverflowMethod = "ellipsis", + width: Optional[int] = None, + min_width: Optional[int] = None, + max_width: Optional[int] = None, + ratio: Optional[int] = None, + no_wrap: bool = False, + ) -> None: ... + + def add_row( + self, + *renderables: Optional[RenderableType], + style: Optional[StyleType] = None, + end_section: bool = False, + ) -> None: ... +``` + +[Tables](./tables.md) + +### Progress Tracking + +Advanced progress bar system with customizable columns, multiple tasks, and real-time updates. + +```python { .api } +class Progress: + def __init__( + self, + *columns: Union[str, ProgressColumn], + console: Optional[Console] = None, + auto_refresh: bool = True, + refresh_per_second: float = 10, + speed_estimate_period: float = 30.0, + transient: bool = False, + redirect_stdout: bool = True, + redirect_stderr: bool = True, + get_time: Optional[GetTimeCallable] = None, + disable: bool = False, + expand: bool = False, + ): ... + + def add_task( + self, + description: str, + start: bool = True, + total: Optional[float] = 100.0, + completed: float = 0, + visible: bool = True, + **fields: Any, + ) -> TaskID: ... + + def update( + self, + task_id: TaskID, + *, + total: Optional[float] = None, + completed: Optional[float] = None, + advance: Optional[float] = None, + description: Optional[str] = None, + visible: Optional[bool] = None, + refresh: bool = False, + **fields: Any, + ) -> None: ... + + def track( + self, + sequence: Iterable[ProgressType], + task_id: Optional[TaskID] = None, + description: str = "Working...", + total: Optional[float] = None, + auto_refresh: bool = True, + console: Optional[Console] = None, + transient: bool = False, + get_time: Optional[GetTimeCallable] = None, + refresh_per_second: float = 10, + style: StyleType = "bar.back", + complete_style: StyleType = "bar.complete", + finished_style: StyleType = "bar.finished", + pulse_style: StyleType = "bar.pulse", + update_period: float = 0.1, + disable: bool = False, + show_speed: bool = True, + ) -> Iterable[ProgressType]: ... +``` + +[Progress Tracking](./progress.md) + +### Syntax Highlighting + +Code syntax highlighting with theme support and extensive language coverage via Pygments. + +```python { .api } +class Syntax: + def __init__( + self, + code: str, + lexer: Optional[Union[Lexer, str]] = None, + *, + theme: Union[str, SyntaxTheme] = "monokai", + dedent: bool = False, + line_numbers: bool = False, + start_line: int = 1, + line_range: Optional[Tuple[int, int]] = None, + highlight_lines: Optional[Set[int]] = None, + code_width: Optional[int] = None, + tab_size: int = 4, + word_wrap: bool = False, + background_color: Optional[str] = None, + indent_guides: bool = False, + padding: PaddingDimensions = 0, + ): ... + + @classmethod + def from_path( + cls, + path: Union[str, PathLike[str]], + encoding: str = "utf-8", + lexer: Optional[Union[Lexer, str]] = None, + theme: Union[str, SyntaxTheme] = "monokai", + dedent: bool = False, + line_numbers: bool = False, + line_range: Optional[Tuple[int, int]] = None, + start_line: int = 1, + highlight_lines: Optional[Set[int]] = None, + code_width: Optional[int] = None, + tab_size: int = 4, + word_wrap: bool = False, + background_color: Optional[str] = None, + indent_guides: bool = False, + padding: PaddingDimensions = 0, + ) -> "Syntax": ... +``` + +[Syntax Highlighting](./syntax.md) + +### Interactive Components + +Live updating displays, user input prompts, and real-time interface components. + +```python { .api } +class Live: + def __init__( + self, + renderable: Optional[RenderableType] = None, + *, + console: Optional[Console] = None, + screen: bool = False, + auto_refresh: bool = True, + refresh_per_second: float = 4, + transient: bool = False, + redirect_stdout: bool = True, + redirect_stderr: bool = True, + vertical_overflow: OverflowMethod = "ellipsis", + get_renderable: Optional[Callable[[], RenderableType]] = None, + ): ... + +class Prompt: + @classmethod + def ask( + cls, + prompt: TextType = "", + *, + console: Optional[Console] = None, + password: bool = False, + choices: Optional[List[str]] = None, + show_default: bool = True, + show_choices: bool = True, + default: Any = ..., + stream: Optional[TextIO] = None, + ) -> Any: ... +``` + +[Interactive Components](./interactive.md) + +### Layout and Positioning + +Advanced layout system for complex terminal UIs with flexible positioning and sizing. + +```python { .api } +class Layout: + def __init__( + self, + renderable: Optional[RenderableType] = None, + *, + name: str = "root", + size: Optional[int] = None, + minimum_size: int = 1, + ratio: int = 1, + visible: bool = True, + ): ... + + def split( + self, + *layouts: Union["Layout", RenderableType], + splitter: Splitter = "column", + ) -> None: ... + + def split_column(self, *layouts: Union["Layout", RenderableType]) -> None: ... + def split_row(self, *layouts: Union["Layout", RenderableType]) -> None: ... +``` + +[Layout System](./layout.md) + +### Markdown Rendering + +CommonMark-compliant markdown rendering with syntax highlighting and Rich formatting. + +```python { .api } +class Markdown: + def __init__( + self, + markup: str, + *, + code_theme: Union[str, SyntaxTheme] = "monokai", + justify: Optional[JustifyMethod] = None, + style: StyleType = "none", + hyperlinks: bool = True, + inline_code_lexer: Optional[str] = None, + inline_code_theme: Optional[Union[str, SyntaxTheme]] = None, + ): ... +``` + +[Markdown](./markdown.md) + +### Panel and Container Components + +Panels, rules, columns, and other container components for organizing content. + +```python { .api } +class Panel: + def __init__( + self, + renderable: RenderableType, + box: Box = box.ROUNDED, + *, + safe_box: Optional[bool] = None, + expand: bool = True, + style: StyleType = "none", + border_style: StyleType = "none", + width: Optional[int] = None, + height: Optional[int] = None, + padding: PaddingDimensions = (0, 1), + highlight: bool = False, + title: Optional[TextType] = None, + title_align: AlignMethod = "center", + subtitle: Optional[TextType] = None, + subtitle_align: AlignMethod = "center", + ): ... + +class Rule: + def __init__( + self, + title: TextType = "", + *, + characters: str = "─", + style: StyleType = "rule.line", + end: str = "\n", + align: AlignMethod = "center", + ): ... +``` + +[Panels and Containers](./containers.md) + +### Utilities and Helpers + +Color handling, measurement, error types, and other utility functions. + +```python { .api } +class Color: + @classmethod + def parse(cls, color: ColorType) -> "Color": ... + + def blend(self, destination: "Color", factor: float) -> "Color": ... + +def inspect( + obj: Any, + *, + console: Optional[Console] = None, + title: Optional[str] = None, + help: bool = False, + methods: bool = False, + docs: bool = True, + private: bool = False, + dunder: bool = False, + sort: bool = True, + all: bool = False, + value: bool = True, +) -> None: ... +``` + +[Utilities](./utilities.md) + +## Types + +```python { .api } +from typing import Union, Optional, IO, Any, Callable, List, Dict, Tuple, Iterable + +# Core types +TextType = Union[str, Text] +StyleType = Union[str, Style] +RenderableType = Union[ConsoleRenderable, RichRenderable, str] +ColorType = Union[int, str, Tuple[int, int, int], Color] + +# Method types +JustifyMethod = Literal["default", "left", "center", "right", "full"] +OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"] +AlignMethod = Literal["left", "center", "right"] +VerticalAlignMethod = Literal["top", "middle", "bottom"] + +# Progress types +TaskID = NewType("TaskID", int) +GetTimeCallable = Callable[[], float] + +# Console types +HighlighterType = Callable[[Union[str, Text]], Text] +FormatTimeCallable = Callable[[datetime], Text] + +# Padding type +PaddingDimensions = Union[ + int, + Tuple[int], + Tuple[int, int], + Tuple[int, int, int], + Tuple[int, int, int, int], +] +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/interactive.md b/.tessl/tiles/tessl/pypi-rich/docs/interactive.md new file mode 100644 index 000000000..cb7d0346c --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/interactive.md @@ -0,0 +1,587 @@ +# Interactive Components + +Live updating displays, user input prompts, and real-time interface components. Rich provides comprehensive interactive capabilities for dynamic terminal applications and user input handling. + +## Capabilities + +### Live Display + +Real-time updating display system for dynamic content. + +```python { .api } +class Live: + """ + Live updating display context manager. + + Args: + renderable: Initial content to display + console: Console instance for output + screen: Use full screen mode + auto_refresh: Enable automatic refresh + refresh_per_second: Refresh rate in Hz + transient: Remove display when exiting context + redirect_stdout: Redirect stdout during live display + redirect_stderr: Redirect stderr during live display + vertical_overflow: Overflow method for vertical content + get_renderable: Function to get current renderable + """ + def __init__( + self, + renderable: Optional[RenderableType] = None, + *, + console: Optional[Console] = None, + screen: bool = False, + auto_refresh: bool = True, + refresh_per_second: float = 4, + transient: bool = False, + redirect_stdout: bool = True, + redirect_stderr: bool = True, + vertical_overflow: OverflowMethod = "ellipsis", + get_renderable: Optional[Callable[[], RenderableType]] = None, + ): ... + + def update( + self, + renderable: RenderableType, + *, + refresh: bool = False + ) -> None: + """ + Update the live display content. + + Args: + renderable: New content to display + refresh: Force immediate refresh + """ + + def refresh(self) -> None: + """Force refresh of the display.""" + + def start(self, refresh: bool = False) -> None: + """ + Start the live display. + + Args: + refresh: Refresh immediately after starting + """ + + def stop(self) -> None: + """Stop the live display.""" + + # Properties + @property + def is_started(self) -> bool: + """Check if live display is active.""" + + @property + def renderable(self) -> RenderableType: + """Get current renderable content.""" + + @property + def console(self) -> Console: + """Get the console instance.""" +``` + +### Status Display + +Status indicator with animated spinner. + +```python { .api } +class Status: + """ + Status display with spinner animation. + + Args: + status: Status message or renderable + console: Console instance for output + spinner: Spinner animation name + spinner_style: Style for spinner + speed: Animation speed multiplier + refresh_per_second: Refresh rate in Hz + """ + def __init__( + self, + status: RenderableType, + *, + console: Optional[Console] = None, + spinner: str = "dots", + spinner_style: StyleType = "status.spinner", + speed: float = 1.0, + refresh_per_second: float = 12.5, + ): ... + + def update( + self, + status: Optional[RenderableType] = None, + *, + spinner: Optional[str] = None, + spinner_style: Optional[StyleType] = None, + speed: Optional[float] = None, + ) -> None: + """ + Update status display. + + Args: + status: New status message + spinner: New spinner style + spinner_style: New spinner styling + speed: New animation speed + """ + + def start(self) -> None: + """Start the status display.""" + + def stop(self) -> None: + """Stop the status display.""" + + # Properties + @property + def renderable(self) -> RenderableType: + """Get current status renderable.""" +``` + +### User Input Prompts + +Interactive prompts for user input with validation. + +```python { .api } +class PromptBase(Generic[PromptType]): + """Base class for user input prompts.""" + + @classmethod + def ask( + cls, + prompt: TextType = "", + *, + console: Optional[Console] = None, + password: bool = False, + choices: Optional[List[str]] = None, + show_default: bool = True, + show_choices: bool = True, + default: Any = ..., + stream: Optional[TextIO] = None, + ) -> PromptType: + """ + Prompt user for input. + + Args: + prompt: Prompt text to display + console: Console instance for I/O + password: Hide input for passwords + choices: Valid choices for input + show_default: Show default value in prompt + show_choices: Show available choices + default: Default value if no input + stream: Input stream or None for stdin + + Returns: + User input converted to appropriate type + """ + + def get_input( + self, + console: Console, + prompt: TextType, + password: bool, + stream: Optional[TextIO] = None, + ) -> str: + """ + Get raw input from user. + + Args: + console: Console for output + prompt: Prompt to display + password: Hide input characters + stream: Input stream + + Returns: + Raw input string + """ + + def check_choice(self, value: str) -> bool: + """ + Check if input matches available choices. + + Args: + value: User input to check + + Returns: + True if valid choice + """ + + def process_response(self, value: str) -> PromptType: + """ + Process and validate user response. + + Args: + value: Raw user input + + Returns: + Processed and validated response + """ + + def on_validate_error(self, value: str, error: ValueError) -> None: + """ + Handle validation errors. + + Args: + value: Invalid input value + error: Validation error + """ + + def render_default(self, default: Any) -> Text: + """ + Render default value for display. + + Args: + default: Default value + + Returns: + Formatted default value + """ + +class Prompt(PromptBase[str]): + """String input prompt.""" + + @classmethod + def ask( + cls, + prompt: TextType = "", + *, + console: Optional[Console] = None, + password: bool = False, + choices: Optional[List[str]] = None, + show_default: bool = True, + show_choices: bool = True, + default: str = "", + stream: Optional[TextIO] = None, + ) -> str: + """ + Prompt for string input. + + Args: + prompt: Prompt text + console: Console instance + password: Hide input + choices: Valid string choices + show_default: Show default in prompt + show_choices: Show available choices + default: Default string value + stream: Input stream + + Returns: + User input string + """ + +class IntPrompt(PromptBase[int]): + """Integer input prompt.""" + + @classmethod + def ask( + cls, + prompt: TextType = "", + *, + console: Optional[Console] = None, + password: bool = False, + choices: Optional[List[int]] = None, + show_default: bool = True, + show_choices: bool = True, + default: int = ..., + stream: Optional[TextIO] = None, + ) -> int: + """ + Prompt for integer input. + + Args: + prompt: Prompt text + console: Console instance + password: Hide input + choices: Valid integer choices + show_default: Show default in prompt + show_choices: Show available choices + default: Default integer value + stream: Input stream + + Returns: + User input as integer + """ + +class FloatPrompt(PromptBase[float]): + """Float input prompt.""" + + @classmethod + def ask( + cls, + prompt: TextType = "", + *, + console: Optional[Console] = None, + password: bool = False, + choices: Optional[List[float]] = None, + show_default: bool = True, + show_choices: bool = True, + default: float = ..., + stream: Optional[TextIO] = None, + ) -> float: + """ + Prompt for float input. + + Args: + prompt: Prompt text + console: Console instance + password: Hide input + choices: Valid float choices + show_default: Show default in prompt + show_choices: Show available choices + default: Default float value + stream: Input stream + + Returns: + User input as float + """ + +class Confirm(PromptBase[bool]): + """Yes/no confirmation prompt.""" + + @classmethod + def ask( + cls, + prompt: TextType = "", + *, + console: Optional[Console] = None, + password: bool = False, + choices: Optional[List[str]] = None, + show_default: bool = True, + show_choices: bool = True, + default: bool = False, + stream: Optional[TextIO] = None, + ) -> bool: + """ + Prompt for yes/no confirmation. + + Args: + prompt: Prompt text + console: Console instance + password: Hide input (not recommended for confirm) + choices: Custom yes/no choices + show_default: Show default in prompt + show_choices: Show y/n choices + default: Default boolean value + stream: Input stream + + Returns: + True for yes, False for no + """ +``` + +### Spinner Animations + +Animated spinner components for loading indicators. + +```python { .api } +class Spinner: + """ + Animated spinner for loading indicators. + + Args: + name: Spinner animation name + text: Text to display with spinner + style: Style for spinner and text + speed: Animation speed multiplier + """ + def __init__( + self, + name: str = "dots", + text: TextType = "", + *, + style: Optional[StyleType] = None, + speed: float = 1.0, + ): ... + + def render(self, time: float) -> RenderableType: + """ + Render spinner at given time. + + Args: + time: Current time for animation + + Returns: + Renderable spinner frame + """ + + # Properties + @property + def name(self) -> str: + """Get spinner name.""" + + @property + def frames(self) -> List[str]: + """Get animation frames.""" + + @property + def interval(self) -> float: + """Get frame interval in seconds.""" +``` + +**Usage Examples:** + +```python +from rich.live import Live +from rich.console import Console +from rich.table import Table +from rich.spinner import Spinner +from rich.status import Status +from rich.prompt import Prompt, IntPrompt, FloatPrompt, Confirm +import time + +console = Console() + +# Basic live display +def generate_table(): + """Generate a sample table.""" + table = Table() + table.add_column("ID") + table.add_column("Name") + table.add_column("Status") + + table.add_row("1", "Task A", "Running") + table.add_row("2", "Task B", "Complete") + table.add_row("3", "Task C", "Pending") + + return table + +with Live(generate_table(), refresh_per_second=4) as live: + for i in range(40): + time.sleep(0.1) + # Update with new content + table = generate_table() + # Add dynamic row + table.add_row(str(i+4), f"Dynamic {i}", "Active") + live.update(table) + +# Live display with periodic updates +from rich.text import Text + +def get_current_time(): + return Text(f"Current time: {time.strftime('%H:%M:%S')}", style="bold green") + +with Live(get_current_time(), refresh_per_second=1) as live: + for _ in range(10): + time.sleep(1) + live.update(get_current_time()) + +# Status with spinner +with console.status("Loading data...") as status: + time.sleep(2) + status.update("Processing data...") + time.sleep(2) + status.update("Finalizing...") + time.sleep(1) + +# Custom status display +status = Status( + "Custom loading...", + spinner="dots", + spinner_style="bold blue" +) + +with status: + time.sleep(3) + +# User input prompts +name = Prompt.ask("What's your name?") +print(f"Hello {name}!") + +age = IntPrompt.ask("What's your age?", default=25) +print(f"You are {age} years old") + +height = FloatPrompt.ask("What's your height in meters?", default=1.75) +print(f"Your height is {height}m") + +# Confirmation prompt +delete_files = Confirm.ask("Delete all files?") +if delete_files: + print("Files would be deleted") +else: + print("Operation cancelled") + +# Prompt with choices +color = Prompt.ask( + "Choose a color", + choices=["red", "green", "blue"], + default="blue" +) +print(f"You chose {color}") + +# Password input +password = Prompt.ask("Enter password", password=True) +print("Password entered (hidden)") + +# Complex live display with multiple updates +from rich.panel import Panel +from rich.columns import Columns + +def create_dashboard(): + """Create a dashboard layout.""" + left_panel = Panel("System Status: OK", title="Status") + right_panel = Panel(f"Time: {time.strftime('%H:%M:%S')}", title="Clock") + + return Columns([left_panel, right_panel]) + +with Live(create_dashboard(), refresh_per_second=1) as live: + for i in range(20): + time.sleep(0.5) + if i % 4 == 0: # Update every 2 seconds + live.update(create_dashboard()) + +# Progress with live updates +from rich.progress import Progress, TaskID + +def live_progress_demo(): + """Demonstrate live progress updates.""" + progress = Progress() + + with Live(progress, refresh_per_second=10) as live: + task1 = progress.add_task("Download", total=1000) + task2 = progress.add_task("Process", total=1000) + + while not progress.finished: + progress.advance(task1, 3) + progress.advance(task2, 1) + time.sleep(0.02) + +# Live display with error handling +def safe_live_display(): + """Live display with error handling.""" + try: + with Live("Starting...", transient=True) as live: + for i in range(5): + live.update(f"Step {i+1}/5") + time.sleep(1) + live.update("Complete!") + time.sleep(1) + except KeyboardInterrupt: + console.print("Display interrupted by user") + +# Interactive menu using prompts +def interactive_menu(): + """Create an interactive menu system.""" + while True: + console.print("\n[bold blue]Main Menu[/bold blue]") + console.print("1. View Status") + console.print("2. Update Settings") + console.print("3. Exit") + + choice = IntPrompt.ask("Choose option", choices=[1, 2, 3]) + + if choice == 1: + console.print("[green]Status: All systems operational[/green]") + elif choice == 2: + console.print("[yellow]Settings updated[/yellow]") + elif choice == 3: + if Confirm.ask("Really exit?"): + break + + console.print("Goodbye!") + +# Example: interactive_menu() +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/layout.md b/.tessl/tiles/tessl/pypi-rich/docs/layout.md new file mode 100644 index 000000000..715873e4a --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/layout.md @@ -0,0 +1,113 @@ +# Layout System + +Advanced layout system for complex terminal UIs with flexible positioning and sizing. Rich provides powerful layout management for creating sophisticated terminal applications with responsive design. + +## Capabilities + +### Layout Class + +Main layout management system with flexible splitting and positioning. + +```python { .api } +class Layout: + """ + Layout management for terminal UIs. + + Args: + renderable: Initial content to display + name: Layout identifier + size: Fixed size or None for flexible + minimum_size: Minimum size constraint + ratio: Size ratio for flexible layouts + visible: Layout visibility + """ + def __init__( + self, + renderable: Optional[RenderableType] = None, + *, + name: str = "root", + size: Optional[int] = None, + minimum_size: int = 1, + ratio: int = 1, + visible: bool = True, + ): ... + + def split( + self, + *layouts: Union["Layout", RenderableType], + splitter: Splitter = "column", + ) -> None: + """ + Split layout into multiple sections. + + Args: + *layouts: Child layouts or renderables + splitter: Split direction ("column" or "row") + """ + + def split_column(self, *layouts: Union["Layout", RenderableType]) -> None: + """Split layout vertically.""" + + def split_row(self, *layouts: Union["Layout", RenderableType]) -> None: + """Split layout horizontally.""" + + def add_split(self, *layouts: Union["Layout", RenderableType]) -> None: + """Add layouts to existing split.""" + + def unsplit(self) -> "Layout": + """Remove split and return to single layout.""" + + def update(self, renderable: RenderableType) -> None: + """Update layout content.""" + + def refresh_screen(self, console: Console, size: ConsoleDimensions) -> None: + """Refresh layout display.""" + +class Splitter(ABC): + """Abstract base for layout splitters.""" + + @abstractmethod + def divide( + self, + children: Sequence[Layout], + size: int + ) -> List[int]: ... + +class RowSplitter(Splitter): + """Horizontal layout splitter.""" + +class ColumnSplitter(Splitter): + """Vertical layout splitter.""" +``` + +**Usage Examples:** + +```python +from rich.console import Console +from rich.layout import Layout +from rich.panel import Panel + +console = Console() + +# Basic layout +layout = Layout() +layout.split_column( + Layout(Panel("Header", style="blue")), + Layout(Panel("Content", style="green")), + Layout(Panel("Footer", style="red")) +) +console.print(layout) + +# Complex layout with ratios +layout = Layout() +layout.split_column( + Layout(Panel("Header"), size=3), + Layout(name="main"), + Layout(Panel("Footer"), size=3) +) +layout["main"].split_row( + Layout(Panel("Left"), name="left"), + Layout(Panel("Right"), name="right") +) +console.print(layout) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/markdown.md b/.tessl/tiles/tessl/pypi-rich/docs/markdown.md new file mode 100644 index 000000000..dd632e5d6 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/markdown.md @@ -0,0 +1,65 @@ +# Markdown + +CommonMark-compliant markdown rendering with syntax highlighting and Rich formatting. Rich provides comprehensive markdown support for displaying formatted documents in the terminal. + +## Capabilities + +### Markdown Class + +Main markdown rendering component with full CommonMark support. + +```python { .api } +class Markdown: + """ + Markdown document renderer. + + Args: + markup: Markdown text to render + code_theme: Theme for code blocks + justify: Text justification + style: Base style for markdown + hyperlinks: Enable hyperlink rendering + inline_code_lexer: Lexer for inline code + inline_code_theme: Theme for inline code + """ + def __init__( + self, + markup: str, + *, + code_theme: Union[str, SyntaxTheme] = "monokai", + justify: Optional[JustifyMethod] = None, + style: StyleType = "none", + hyperlinks: bool = True, + inline_code_lexer: Optional[str] = None, + inline_code_theme: Optional[Union[str, SyntaxTheme]] = None, + ): ... +``` + +**Usage Examples:** + +```python +from rich.console import Console +from rich.markdown import Markdown + +console = Console() + +markdown_text = """ +# My Document + +This is **bold** and *italic* text. + +## Code Example + +```python +def hello(): + print("Hello, World!") +``` + +- List item 1 +- List item 2 +- List item 3 +""" + +md = Markdown(markdown_text) +console.print(md) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/progress.md b/.tessl/tiles/tessl/pypi-rich/docs/progress.md new file mode 100644 index 000000000..e01e4d6ec --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/progress.md @@ -0,0 +1,608 @@ +# Progress Tracking + +Advanced progress bar system with customizable columns, multiple tasks, and real-time updates. Rich provides comprehensive progress tracking capabilities for long-running operations with flexible display options. + +## Capabilities + +### Progress Class + +Main progress tracking system with support for multiple concurrent tasks. + +```python { .api } +class Progress: + """ + Multi-task progress tracker with customizable display. + + Args: + *columns: Progress display columns + console: Console instance for output + auto_refresh: Enable automatic refresh + refresh_per_second: Refresh rate in Hz + speed_estimate_period: Period for speed calculation + transient: Remove progress display when complete + redirect_stdout: Redirect stdout during progress + redirect_stderr: Redirect stderr during progress + get_time: Function to get current time + disable: Disable progress display + expand: Expand progress bar to fit console width + """ + def __init__( + self, + *columns: Union[str, ProgressColumn], + console: Optional[Console] = None, + auto_refresh: bool = True, + refresh_per_second: float = 10, + speed_estimate_period: float = 30.0, + transient: bool = False, + redirect_stdout: bool = True, + redirect_stderr: bool = True, + get_time: Optional[GetTimeCallable] = None, + disable: bool = False, + expand: bool = False, + ): ... + + def add_task( + self, + description: str, + start: bool = True, + total: Optional[float] = 100.0, + completed: float = 0, + visible: bool = True, + **fields: Any, + ) -> TaskID: + """ + Add a new progress task. + + Args: + description: Task description + start: Start the task immediately + total: Total amount of work or None for indeterminate + completed: Initial completed amount + visible: Show task in display + **fields: Additional custom fields + + Returns: + Task ID for future operations + """ + + def remove_task(self, task_id: TaskID) -> None: + """ + Remove a task from progress tracking. + + Args: + task_id: ID of task to remove + """ + + def update( + self, + task_id: TaskID, + *, + total: Optional[float] = None, + completed: Optional[float] = None, + advance: Optional[float] = None, + description: Optional[str] = None, + visible: Optional[bool] = None, + refresh: bool = False, + **fields: Any, + ) -> None: + """ + Update a progress task. + + Args: + task_id: Task to update + total: New total amount + completed: New completed amount + advance: Amount to advance by + description: New description + visible: Show/hide task + refresh: Force display refresh + **fields: Update custom fields + """ + + def advance(self, task_id: TaskID, advance: float = 1.0) -> None: + """ + Advance a task by specified amount. + + Args: + task_id: Task to advance + advance: Amount to advance by + """ + + def start_task(self, task_id: TaskID) -> None: + """ + Start a paused task. + + Args: + task_id: Task to start + """ + + def stop_task(self, task_id: TaskID) -> None: + """ + Stop/pause a task. + + Args: + task_id: Task to stop + """ + + def reset( + self, + task_id: TaskID, + *, + start: bool = True, + total: Optional[float] = None, + completed: float = 0, + visible: Optional[bool] = None, + description: Optional[str] = None, + **fields: Any, + ) -> None: + """ + Reset a task to initial state. + + Args: + task_id: Task to reset + start: Start task after reset + total: New total amount + completed: Reset completed amount + visible: Task visibility + description: New description + **fields: Reset custom fields + """ + + def get_task(self, task_id: TaskID) -> Task: + """ + Get task information. + + Args: + task_id: Task ID + + Returns: + Task object with current state + """ + + def refresh(self) -> None: + """Force refresh of progress display.""" + + def start(self) -> None: + """Start progress display.""" + + def stop(self) -> None: + """Stop progress display.""" + + def track( + self, + sequence: Iterable[ProgressType], + task_id: Optional[TaskID] = None, + description: str = "Working...", + total: Optional[float] = None, + auto_refresh: bool = True, + console: Optional[Console] = None, + transient: bool = False, + get_time: Optional[GetTimeCallable] = None, + refresh_per_second: float = 10, + style: StyleType = "bar.back", + complete_style: StyleType = "bar.complete", + finished_style: StyleType = "bar.finished", + pulse_style: StyleType = "bar.pulse", + update_period: float = 0.1, + disable: bool = False, + show_speed: bool = True, + ) -> Iterable[ProgressType]: + """ + Track progress of an iterable. + + Args: + sequence: Iterable to track + task_id: Existing task ID or None for new task + description: Task description + total: Total items or None to auto-detect + auto_refresh: Enable automatic refresh + console: Console for output + transient: Remove when complete + get_time: Time function + refresh_per_second: Refresh rate + style: Progress bar style + complete_style: Completed portion style + finished_style: Finished bar style + pulse_style: Indeterminate pulse style + update_period: Update frequency + disable: Disable progress display + show_speed: Show processing speed + + Returns: + Iterator that yields items while tracking progress + """ + + @contextmanager + def wrap_file( + self, + file: BinaryIO, + total: int, + *, + task_id: Optional[TaskID] = None, + description: str = "Reading...", + ) -> Iterator[BinaryIO]: + """ + Wrap a file for progress tracking. + + Args: + file: File object to wrap + total: Total file size in bytes + task_id: Existing task ID or None for new task + description: Task description + + Yields: + Wrapped file object that updates progress + """ + + @contextmanager + def open( + self, + file: Union[str, "PathLike[str]", IO[bytes]], + mode: str = "rb", + *, + buffering: int = -1, + encoding: Optional[str] = None, + errors: Optional[str] = None, + newline: Optional[str] = None, + description: str = "Reading...", + auto_refresh: bool = True, + console: Optional[Console] = None, + transient: bool = False, + get_time: Optional[GetTimeCallable] = None, + refresh_per_second: float = 10, + style: StyleType = "bar.back", + complete_style: StyleType = "bar.complete", + finished_style: StyleType = "bar.finished", + pulse_style: StyleType = "bar.pulse", + disable: bool = False, + ) -> Iterator[IO[Any]]: + """ + Open a file with progress tracking. + + Args: + file: File path or file object + mode: File open mode + buffering: Buffer size + encoding: Text encoding + errors: Error handling + newline: Newline handling + description: Task description + auto_refresh: Enable automatic refresh + console: Console for output + transient: Remove when complete + get_time: Time function + refresh_per_second: Refresh rate + style: Progress bar style + complete_style: Completed portion style + finished_style: Finished bar style + pulse_style: Indeterminate pulse style + disable: Disable progress display + + Yields: + File object with progress tracking + """ + + # Properties + @property + def tasks(self) -> List[Task]: + """Get list of all tasks.""" + + @property + def task_ids(self) -> List[TaskID]: + """Get list of all task IDs.""" + + @property + def finished(self) -> bool: + """Check if all tasks are finished.""" + + @property + def live(self) -> Live: + """Get the underlying Live display.""" +``` + +### Task Class + +Task information and state. + +```python { .api } +class Task: + """ + Progress task data and state. + + Attributes: + id: Unique task identifier + description: Task description text + total: Total amount of work or None for indeterminate + completed: Amount of work completed + visible: Whether task is visible in display + fields: Custom data fields + created_time: Time when task was created + started_time: Time when task was started + stopped_time: Time when task was stopped + finished_time: Time when task finished + """ + id: TaskID + description: str + total: Optional[float] + completed: float + visible: bool + fields: Dict[str, Any] + created_time: float + started_time: Optional[float] + stopped_time: Optional[float] + finished_time: Optional[float] + + @property + def elapsed(self) -> Optional[float]: + """Get elapsed time since task started.""" + + @property + def finished(self) -> bool: + """Check if task is finished.""" + + @property + def percentage(self) -> float: + """Get completion percentage (0-100).""" + + @property + def speed(self) -> Optional[float]: + """Get current processing speed.""" + + @property + def time_remaining(self) -> Optional[float]: + """Get estimated time remaining.""" +``` + +### Progress Columns + +Customizable display columns for progress bars. + +```python { .api } +class ProgressColumn(ABC): + """Base class for progress display columns.""" + + @abstractmethod + def render(self, task: Task) -> RenderableType: + """ + Render column content for a task. + + Args: + task: Task to render + + Returns: + Renderable content + """ + +class RenderableColumn(ProgressColumn): + """Column that displays a fixed renderable.""" + + def __init__(self, renderable: RenderableType, *, table_column: Optional[Column] = None): ... + +class SpinnerColumn(ProgressColumn): + """Column with animated spinner.""" + + def __init__( + self, + spinner_name: str = "dots", + style: Optional[StyleType] = None, + speed: float = 1.0, + finished_text: TextType = "✓", + table_column: Optional[Column] = None, + ): ... + +class TextColumn(ProgressColumn): + """Column displaying task text/description.""" + + def __init__( + self, + text_format: str = "[progress.description]{task.description}", + style: StyleType = "", + justify: JustifyMethod = "left", + markup: bool = True, + highlighter: Optional[Highlighter] = None, + table_column: Optional[Column] = None, + ): ... + +class BarColumn(ProgressColumn): + """Progress bar column.""" + + def __init__( + self, + bar_width: Optional[int] = 40, + style: StyleType = "bar.back", + complete_style: StyleType = "bar.complete", + finished_style: StyleType = "bar.finished", + pulse_style: StyleType = "bar.pulse", + table_column: Optional[Column] = None, + ): ... + +class TaskProgressColumn(TextColumn): + """Column showing task progress percentage.""" + + def __init__( + self, + text_format: str = "[progress.percentage]{task.percentage:>3.1f}%", + style: StyleType = "", + justify: JustifyMethod = "left", + markup: bool = True, + highlighter: Optional[Highlighter] = None, + table_column: Optional[Column] = None, + ): ... + +class TimeElapsedColumn(ProgressColumn): + """Column showing elapsed time.""" + + def __init__(self, table_column: Optional[Column] = None): ... + +class TimeRemainingColumn(ProgressColumn): + """Column showing estimated time remaining.""" + + def __init__( + self, + compact: bool = False, + elapsed_when_finished: bool = False, + table_column: Optional[Column] = None, + ): ... + +class FileSizeColumn(ProgressColumn): + """Column showing file size completed.""" + + def __init__(self, table_column: Optional[Column] = None): ... + +class TotalFileSizeColumn(ProgressColumn): + """Column showing total file size.""" + + def __init__(self, table_column: Optional[Column] = None): ... + +class MofNCompleteColumn(ProgressColumn): + """Column showing 'M of N' completion.""" + + def __init__( + self, + separator: str = "/", + table_column: Optional[Column] = None, + ): ... + +class DownloadColumn(ProgressColumn): + """Column for download progress with speed.""" + + def __init__( + self, + binary_units: bool = False, + table_column: Optional[Column] = None, + ): ... + +class TransferSpeedColumn(ProgressColumn): + """Column showing transfer speed.""" + + def __init__(self, table_column: Optional[Column] = None): ... +``` + +**Usage Examples:** + +```python +from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn, TimeElapsedColumn +import time + +# Basic progress bar +with Progress() as progress: + task = progress.add_task("Processing...", total=100) + + for i in range(100): + time.sleep(0.1) + progress.update(task, advance=1) + +# Custom progress layout +progress = Progress( + SpinnerColumn(), + TextColumn("[progress.description]{task.description}"), + BarColumn(), + TaskProgressColumn(), + TimeElapsedColumn(), +) + +with progress: + task1 = progress.add_task("Download", total=1000) + task2 = progress.add_task("Process", total=500) + + # Simulate work + for i in range(1000): + time.sleep(0.01) + progress.update(task1, advance=1) + if i % 2 == 0: + progress.update(task2, advance=1) + +# Multiple concurrent tasks +with Progress() as progress: + tasks = [ + progress.add_task("Task 1", total=100), + progress.add_task("Task 2", total=200), + progress.add_task("Task 3", total=150), + ] + + # Process tasks concurrently + import threading + + def work(task_id, total): + for i in range(total): + time.sleep(0.05) + progress.update(task_id, advance=1) + + threads = [] + for task_id, total in zip(tasks, [100, 200, 150]): + thread = threading.Thread(target=work, args=(task_id, total)) + threads.append(thread) + thread.start() + + for thread in threads: + thread.join() + +# Track iterable +from rich.progress import track + +items = range(100) +for item in track(items, description="Processing items..."): + time.sleep(0.1) # Simulate work + +# File download simulation +from rich.progress import Progress, DownloadColumn, TransferSpeedColumn + +def download_file(url, size): + """Simulate file download.""" + progress = Progress( + TextColumn("[bold blue]{task.description}", justify="right"), + BarColumn(bar_width=None), + "[progress.percentage]{task.percentage:>3.1f}%", + "•", + DownloadColumn(), + "•", + TransferSpeedColumn(), + "•", + TimeElapsedColumn(), + ) + + with progress: + task = progress.add_task("download", filename=url, total=size) + for chunk_size in [500, 600, 300, 400, 800, 200]: + time.sleep(0.5) # Simulate network delay + progress.update(task, advance=chunk_size) + +# Example: download_file("example.zip", 3000) + +# Indeterminate progress (no known total) +with Progress() as progress: + task = progress.add_task("Scanning...", total=None) + + for i in range(50): + time.sleep(0.1) + progress.update(task, advance=1) # Still advances for timing + +# Progress with custom fields +with Progress() as progress: + task = progress.add_task("Custom Task", total=100, status="Starting") + + for i in range(100): + status = f"Processing item {i+1}" + progress.update(task, advance=1, status=status) + time.sleep(0.05) + +# File processing with progress +from rich.progress import Progress + +def process_files(file_paths): + with Progress() as progress: + main_task = progress.add_task("Processing files", total=len(file_paths)) + + for file_path in file_paths: + # Process individual file + file_task = progress.add_task(f"Processing {file_path}", total=100) + + for chunk in range(100): + time.sleep(0.01) # Simulate file processing + progress.update(file_task, advance=1) + + progress.update(main_task, advance=1) + progress.remove_task(file_task) + +# Example: process_files(["file1.txt", "file2.txt", "file3.txt"]) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/syntax.md b/.tessl/tiles/tessl/pypi-rich/docs/syntax.md new file mode 100644 index 000000000..8e45d46cd --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/syntax.md @@ -0,0 +1,411 @@ +# Syntax Highlighting + +Code syntax highlighting with theme support and extensive language coverage via Pygments. Rich provides comprehensive syntax highlighting capabilities for displaying source code with proper formatting and color schemes. + +## Capabilities + +### Syntax Class + +Main syntax highlighting component with theme and formatting options. + +```python { .api } +class Syntax: + """ + Syntax highlighted code display. + + Args: + code: Source code to highlight + lexer: Pygments lexer name or instance + theme: Color theme name or SyntaxTheme instance + dedent: Remove common leading whitespace + line_numbers: Show line numbers + start_line: Starting line number + line_range: Range of lines to highlight (start, end) + highlight_lines: Set of line numbers to highlight + code_width: Fixed width for code or None for auto + tab_size: Size of tab characters in spaces + word_wrap: Enable word wrapping + background_color: Override background color + indent_guides: Show indentation guides + padding: Padding around code block + """ + def __init__( + self, + code: str, + lexer: Optional[Union[Lexer, str]] = None, + *, + theme: Union[str, SyntaxTheme] = "monokai", + dedent: bool = False, + line_numbers: bool = False, + start_line: int = 1, + line_range: Optional[Tuple[int, int]] = None, + highlight_lines: Optional[Set[int]] = None, + code_width: Optional[int] = None, + tab_size: int = 4, + word_wrap: bool = False, + background_color: Optional[str] = None, + indent_guides: bool = False, + padding: PaddingDimensions = 0, + ): ... + + @classmethod + def from_path( + cls, + path: Union[str, PathLike[str]], + encoding: str = "utf-8", + lexer: Optional[Union[Lexer, str]] = None, + theme: Union[str, SyntaxTheme] = "monokai", + dedent: bool = False, + line_numbers: bool = False, + line_range: Optional[Tuple[int, int]] = None, + start_line: int = 1, + highlight_lines: Optional[Set[int]] = None, + code_width: Optional[int] = None, + tab_size: int = 4, + word_wrap: bool = False, + background_color: Optional[str] = None, + indent_guides: bool = False, + padding: PaddingDimensions = 0, + ) -> "Syntax": + """ + Create Syntax from file path. + + Args: + path: Path to source code file + encoding: File encoding + lexer: Lexer name or None to auto-detect + theme: Syntax theme + dedent: Remove common indentation + line_numbers: Show line numbers + line_range: Range of lines to show + start_line: Starting line number + highlight_lines: Lines to highlight + code_width: Fixed code width + tab_size: Tab size in spaces + word_wrap: Enable word wrapping + background_color: Background color override + indent_guides: Show indentation guides + padding: Code block padding + + Returns: + New Syntax instance with file contents + """ + + @classmethod + def guess_lexer(cls, path: Union[str, PathLike[str]], code: str) -> str: + """ + Guess lexer from file path and content. + + Args: + path: File path for extension hints + code: Source code content + + Returns: + Lexer name for the code + """ + + def highlight(self, code: str) -> Text: + """ + Apply syntax highlighting to code. + + Args: + code: Source code to highlight + + Returns: + Highlighted text object + """ + + # Properties + @property + def lexer(self) -> Optional[Lexer]: + """Get the Pygments lexer.""" + + @property + def theme(self) -> SyntaxTheme: + """Get the syntax theme.""" +``` + +### Syntax Themes + +Theme system for syntax highlighting colors. + +```python { .api } +class SyntaxTheme(ABC): + """Abstract base for syntax highlighting themes.""" + + @abstractmethod + def get_style_for_token(self, token_type: TokenType) -> Style: + """ + Get style for a token type. + + Args: + token_type: Pygments token type + + Returns: + Rich Style for the token + """ + + @abstractmethod + def get_background_style(self) -> Style: + """ + Get background style for code blocks. + + Returns: + Rich Style for background + """ + +class PygmentsSyntaxTheme(SyntaxTheme): + """Syntax theme based on Pygments themes.""" + + def __init__( + self, + theme: Union[str, Type[PygmentsStyle]] + ): ... + + @classmethod + def get_theme_names(cls) -> List[str]: + """ + Get list of available Pygments theme names. + + Returns: + List of theme names + """ + +class ANSISyntaxTheme(SyntaxTheme): + """Syntax theme using ANSI color codes.""" + + def __init__(self, style_map: Dict[TokenType, Style]): ... + +# Default themes +DEFAULT_THEME: str # Default theme name +RICH_SYNTAX_THEMES: Dict[str, SyntaxTheme] # Available themes +``` + +**Usage Examples:** + +```python +from rich.console import Console +from rich.syntax import Syntax +import tempfile +import os + +console = Console() + +# Basic syntax highlighting +python_code = ''' +def fibonacci(n): + """Generate Fibonacci sequence up to n.""" + a, b = 0, 1 + while a < n: + print(a, end=' ') + a, b = b, a + b + print() + +fibonacci(100) +''' + +syntax = Syntax(python_code, "python") +console.print(syntax) + +# Syntax with line numbers +syntax = Syntax( + python_code, + "python", + line_numbers=True, + start_line=1 +) +console.print(syntax) + +# Highlight specific lines +syntax = Syntax( + python_code, + "python", + line_numbers=True, + highlight_lines={2, 4, 5} # Highlight lines 2, 4, and 5 +) +console.print(syntax) + +# Different themes +themes = ["monokai", "github-dark", "one-dark", "solarized-light"] +for theme in themes: + console.print(f"\n[bold]Theme: {theme}[/bold]") + syntax = Syntax(python_code, "python", theme=theme, line_numbers=True) + console.print(syntax) + +# Load from file +# Create temporary file +with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: + f.write(python_code) + temp_path = f.name + +try: + # Load and highlight from file + syntax = Syntax.from_path( + temp_path, + line_numbers=True, + theme="monokai" + ) + console.print(syntax) +finally: + os.unlink(temp_path) + +# JavaScript example +js_code = ''' +function quickSort(arr) { + if (arr.length <= 1) { + return arr; + } + + const pivot = arr[Math.floor(arr.length / 2)]; + const left = arr.filter(x => x < pivot); + const middle = arr.filter(x => x === pivot); + const right = arr.filter(x => x > pivot); + + return [...quickSort(left), ...middle, ...quickSort(right)]; +} + +const numbers = [64, 34, 25, 12, 22, 11, 90]; +console.log(quickSort(numbers)); +''' + +syntax = Syntax(js_code, "javascript", theme="github-dark", line_numbers=True) +console.print(syntax) + +# SQL example with custom settings +sql_code = ''' +SELECT + customers.name, + orders.order_date, + products.product_name, + order_items.quantity * products.price AS total_price +FROM customers +JOIN orders ON customers.customer_id = orders.customer_id +JOIN order_items ON orders.order_id = order_items.order_id +JOIN products ON order_items.product_id = products.product_id +WHERE orders.order_date >= '2024-01-01' +ORDER BY orders.order_date DESC, total_price DESC; +''' + +syntax = Syntax( + sql_code, + "sql", + theme="one-dark", + line_numbers=True, + word_wrap=True, + indent_guides=True +) +console.print(syntax) + +# Show line range +long_code = ''' +import os +import sys +import json +import requests +from datetime import datetime +from typing import List, Dict, Optional + +class DataProcessor: + def __init__(self, config_path: str): + self.config = self.load_config(config_path) + self.session = requests.Session() + + def load_config(self, path: str) -> Dict: + with open(path, 'r') as f: + return json.load(f) + + def process_data(self, data: List[Dict]) -> List[Dict]: + results = [] + for item in data: + processed = self.transform_item(item) + if self.validate_item(processed): + results.append(processed) + return results + + def transform_item(self, item: Dict) -> Dict: + # Transform logic here + return { + 'id': item.get('id'), + 'timestamp': datetime.now().isoformat(), + 'value': item.get('value', 0) * 2 + } + + def validate_item(self, item: Dict) -> bool: + return all(key in item for key in ['id', 'timestamp', 'value']) +''' + +# Show only lines 10-20 +syntax = Syntax( + long_code, + "python", + line_numbers=True, + line_range=(10, 20), + start_line=10, + theme="monokai" +) +console.print(syntax) + +# Code with custom background +syntax = Syntax( + python_code, + "python", + theme="github-dark", + background_color="black", + padding=1 +) +console.print(syntax) + +# Dedent code (remove common indentation) +indented_code = ''' + def helper_function(): + print("This is indented") + return True + + def another_function(): + if helper_function(): + print("Success!") +''' + +syntax = Syntax( + indented_code, + "python", + dedent=True, + line_numbers=True +) +console.print(syntax) + +# Auto-detect lexer from filename +def highlight_file_content(filename: str, content: str): + """Highlight code with auto-detected lexer.""" + lexer = Syntax.guess_lexer(filename, content) + syntax = Syntax(content, lexer, line_numbers=True) + console.print(f"[bold]File: {filename} (Lexer: {lexer})[/bold]") + console.print(syntax) + +# Examples of auto-detection +highlight_file_content("script.py", "print('Hello, World!')") +highlight_file_content("style.css", "body { color: red; }") +highlight_file_content("config.json", '{"name": "example", "value": 42}') + +# Syntax in panels +from rich.panel import Panel + +code_panel = Panel( + Syntax(python_code, "python", theme="monokai"), + title="Python Code", + border_style="blue" +) +console.print(code_panel) + +# Multiple code blocks in columns +from rich.columns import Columns + +python_syntax = Syntax("print('Python')", "python", theme="github-dark") +js_syntax = Syntax("console.log('JavaScript');", "javascript", theme="github-dark") + +columns = Columns([ + Panel(python_syntax, title="Python"), + Panel(js_syntax, title="JavaScript") +]) +console.print(columns) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/tables.md b/.tessl/tiles/tessl/pypi-rich/docs/tables.md new file mode 100644 index 000000000..19083a7ef --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/tables.md @@ -0,0 +1,378 @@ +# Tables + +Comprehensive table system with flexible columns, styling, borders, and automatic sizing. Rich tables support complex layouts, custom formatting, and responsive design with automatic width calculation. + +## Capabilities + +### Table Class + +Advanced table rendering with comprehensive formatting and layout options. + +```python { .api } +class Table: + """ + Renderable table with automatic sizing and formatting. + + Args: + *columns: Column definitions or header strings + title: Title displayed above the table + caption: Caption displayed below the table + width: Fixed table width or None for automatic + min_width: Minimum table width + box: Box style for borders + safe_box: Use safe box characters for compatibility + padding: Cell padding (top, right, bottom, left) + collapse_padding: Collapse padding between cells + pad_edge: Add padding to table edges + expand: Expand table to fill available width + show_header: Display header row + show_footer: Display footer row + show_edge: Display outer borders + show_lines: Display lines between rows + leading: Number of blank lines between rows + style: Default table style + row_styles: Alternating row styles + header_style: Header row style + footer_style: Footer row style + border_style: Border style + title_style: Title style + caption_style: Caption style + title_justify: Title justification + caption_justify: Caption justification + highlight: Enable cell content highlighting + """ + def __init__( + self, + *columns: Union[Column, str], + title: Optional[TextType] = None, + caption: Optional[TextType] = None, + width: Optional[int] = None, + min_width: Optional[int] = None, + box: Optional[Box] = box.HEAVY_HEAD, + safe_box: Optional[bool] = None, + padding: PaddingDimensions = (0, 1), + collapse_padding: bool = False, + pad_edge: bool = True, + expand: bool = False, + show_header: bool = True, + show_footer: bool = False, + show_edge: bool = True, + show_lines: bool = False, + leading: int = 0, + style: StyleType = "", + row_styles: Optional[Iterable[StyleType]] = None, + header_style: Optional[StyleType] = None, + footer_style: Optional[StyleType] = None, + border_style: Optional[StyleType] = None, + title_style: Optional[StyleType] = None, + caption_style: Optional[StyleType] = None, + title_justify: JustifyMethod = "center", + caption_justify: JustifyMethod = "center", + highlight: bool = False, + ): ... + + def add_column( + self, + header: RenderableType = "", + *, + footer: RenderableType = "", + header_style: Optional[StyleType] = None, + footer_style: Optional[StyleType] = None, + style: Optional[StyleType] = None, + justify: JustifyMethod = "left", + vertical: VerticalAlignMethod = "top", + overflow: OverflowMethod = "ellipsis", + width: Optional[int] = None, + min_width: Optional[int] = None, + max_width: Optional[int] = None, + ratio: Optional[int] = None, + no_wrap: bool = False, + ) -> None: + """ + Add a column to the table. + + Args: + header: Column header content + footer: Column footer content + header_style: Style for header cell + footer_style: Style for footer cell + style: Default style for column cells + justify: Text justification in column + vertical: Vertical alignment in column + overflow: Overflow handling method + width: Fixed column width + min_width: Minimum column width + max_width: Maximum column width + ratio: Column width ratio for flexible sizing + no_wrap: Disable text wrapping in column + """ + + def add_row( + self, + *renderables: Optional[RenderableType], + style: Optional[StyleType] = None, + end_section: bool = False, + ) -> None: + """ + Add a row to the table. + + Args: + *renderables: Cell contents for each column + style: Style for the entire row + end_section: Mark this row as end of a section + """ + + def add_section(self) -> None: + """Add a section break (line) after the current row.""" + + @classmethod + def grid( + cls, + *, + padding: PaddingDimensions = 0, + collapse_padding: bool = True, + pad_edge: bool = False, + expand: bool = False, + ) -> "Table": + """ + Create a table configured as a grid (no borders). + + Args: + padding: Cell padding + collapse_padding: Collapse padding between cells + pad_edge: Add padding to grid edges + expand: Expand grid to fill available width + + Returns: + New Table configured as grid + """ + + # Properties + @property + def columns(self) -> List[Column]: + """Get list of table columns.""" + + @property + def rows(self) -> List[Row]: + """Get list of table rows.""" + + @property + def row_count(self) -> int: + """Get number of rows in table.""" +``` + +### Column Class + +Column definition with formatting and sizing options. + +```python { .api } +@dataclass +class Column: + """ + Table column definition. + + Attributes: + header: Header content for the column + footer: Footer content for the column + header_style: Style for header cell + footer_style: Style for footer cell + style: Default style for column cells + justify: Text justification method + vertical: Vertical alignment method + overflow: Overflow handling method + width: Fixed column width + min_width: Minimum column width + max_width: Maximum column width + ratio: Width ratio for flexible sizing + no_wrap: Disable text wrapping + """ + header: RenderableType = "" + footer: RenderableType = "" + header_style: StyleType = "" + footer_style: StyleType = "" + style: StyleType = "" + justify: JustifyMethod = "left" + vertical: VerticalAlignMethod = "top" + overflow: OverflowMethod = "ellipsis" + width: Optional[int] = None + min_width: Optional[int] = None + max_width: Optional[int] = None + ratio: Optional[int] = None + no_wrap: bool = False +``` + +### Row Class + +Table row data container. + +```python { .api } +@dataclass +class Row: + """ + Table row data. + + Attributes: + style: Style for the entire row + end_section: Whether this row ends a section + """ + style: Optional[StyleType] = None + end_section: bool = False +``` + +### Box Styles + +Predefined box drawing styles for table borders. + +```python { .api } +class Box: + """Box drawing character set for table borders.""" + + def __init__( + self, + box: str, + *, + ascii: bool = False, + ): ... + +# Predefined box styles +ASCII: Box # ASCII characters only +SQUARE: Box # Square corners +MINIMAL: Box # Minimal borders +SIMPLE: Box # Simple lines +SIMPLE_HEAD: Box # Simple with header separator +SIMPLE_HEAVY: Box # Simple with heavy lines +HORIZONTALS: Box # Horizontal lines only +ROUNDED: Box # Rounded corners +HEAVY: Box # Heavy lines +HEAVY_EDGE: Box # Heavy outer edge +HEAVY_HEAD: Box # Heavy header separator +DOUBLE: Box # Double lines +DOUBLE_EDGE: Box # Double outer edge +``` + +**Usage Examples:** + +```python +from rich.console import Console +from rich.table import Table, Column +from rich import box + +console = Console() + +# Basic table +table = Table() +table.add_column("Name") +table.add_column("Age") +table.add_column("City") + +table.add_row("Alice", "25", "New York") +table.add_row("Bob", "30", "London") +table.add_row("Charlie", "35", "Tokyo") + +console.print(table) + +# Styled table with title and caption +table = Table( + title="Employee Directory", + caption="Last updated: 2024-01-01", + box=box.ROUNDED, + title_style="bold blue", + caption_style="italic dim" +) + +table.add_column("ID", justify="center", style="cyan", no_wrap=True) +table.add_column("Name", style="magenta") +table.add_column("Department", justify="right", style="green") +table.add_column("Salary", justify="right", style="yellow") + +table.add_row("001", "John Doe", "Engineering", "$75,000") +table.add_row("002", "Jane Smith", "Marketing", "$65,000") +table.add_row("003", "Bob Johnson", "Sales", "$55,000") + +console.print(table) + +# Table with column configuration +table = Table(show_header=True, header_style="bold red") + +# Add columns with specific formatting +table.add_column( + "Product", + header_style="bold blue", + style="cyan", + min_width=10, + max_width=20 +) +table.add_column( + "Price", + justify="right", + style="green", + header_style="bold green" +) +table.add_column( + "Status", + justify="center", + style="yellow", + vertical="middle" +) + +table.add_row("Laptop", "$999.99", "In Stock") +table.add_row("Mouse", "$29.99", "Out of Stock") +table.add_row("Keyboard", "$79.99", "In Stock") + +console.print(table) + +# Grid layout (no borders) +grid = Table.grid(padding=1) +grid.add_column(style="red", justify="right") +grid.add_column(style="blue") +grid.add_column(style="green") + +grid.add_row("Label 1:", "Value A", "Extra Info") +grid.add_row("Label 2:", "Value B", "More Info") +grid.add_row("Label 3:", "Value C", "Additional") + +console.print(grid) + +# Table with sections +table = Table() +table.add_column("Category") +table.add_column("Item") +table.add_column("Count") + +table.add_row("Fruits", "Apples", "10") +table.add_row("Fruits", "Oranges", "15") +table.add_section() # Add section break + +table.add_row("Vegetables", "Carrots", "8") +table.add_row("Vegetables", "Lettuce", "12") + +console.print(table) + +# Responsive table with ratios +table = Table(expand=True) +table.add_column("Description", ratio=3) # 3/4 of available width +table.add_column("Value", ratio=1, justify="right") # 1/4 of width + +table.add_row("Very long description that will wrap", "100") +table.add_row("Short desc", "200") + +console.print(table) + +# Table with rich content +from rich.text import Text +from rich.panel import Panel + +table = Table() +table.add_column("Rich Content") +table.add_column("Status") + +# Add styled text +styled_text = Text("Important Item", style="bold red") +table.add_row(styled_text, "Active") + +# Add panel as cell content +panel = Panel("Wrapped Content", style="blue") +table.add_row(panel, "Pending") + +console.print(table) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/text-styling.md b/.tessl/tiles/tessl/pypi-rich/docs/text-styling.md new file mode 100644 index 000000000..de7acf168 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/text-styling.md @@ -0,0 +1,739 @@ +# Text and Styling + +Advanced text objects with precise styling control, markup support, and comprehensive formatting options. Rich provides powerful text handling with styled spans, color support, and flexible rendering capabilities. + +## Capabilities + +### Text Class + +Rich text objects with styling spans and advanced formatting capabilities. + +```python { .api } +class Text: + """ + Styled text with Rich formatting capabilities. + + Args: + text: Plain text content + style: Default style for the text + justify: Text justification method + overflow: Overflow handling method + no_wrap: Disable text wrapping + end: String to append at end + tab_size: Size of tab characters + spans: List of styled spans + """ + def __init__( + self, + text: str = "", + style: StyleType = "", + *, + justify: Optional[JustifyMethod] = None, + overflow: OverflowMethod = "fold", + no_wrap: Optional[bool] = None, + end: str = "\n", + tab_size: Optional[int] = 8, + spans: Optional[List[Span]] = None, + ): ... + + def stylize( + self, + style: StyleType, + start: int = 0, + end: Optional[int] = None, + ) -> None: + """ + Apply style to a range of text. + + Args: + style: Style to apply + start: Start character index + end: End character index or None for end of text + """ + + def stylize_range( + self, + style: StyleType, + start: int = 0, + end: Optional[int] = None, + ) -> None: + """ + Apply style to a range of text (alias for stylize). + + Args: + style: Style to apply + start: Start character index + end: End character index or None for end of text + """ + + def stylize_before( + self, + style: StyleType, + start: int = 0, + end: Optional[int] = None, + ) -> "Text": + """ + Apply style before existing styles in a range. + + Args: + style: Style to apply + start: Start character index + end: End character index or None for end of text + + Returns: + New Text instance with applied style + """ + + def stylize_after( + self, + style: StyleType, + start: int = 0, + end: Optional[int] = None, + ) -> "Text": + """ + Apply style after existing styles in a range. + + Args: + style: Style to apply + start: Start character index + end: End character index or None for end of text + + Returns: + New Text instance with applied style + """ + + def highlight_words( + self, + words: Iterable[str], + style: StyleType, + *, + case_sensitive: bool = True, + ) -> int: + """ + Highlight specific words in the text. + + Args: + words: Words to highlight + style: Style to apply to highlighted words + case_sensitive: Enable case-sensitive matching + + Returns: + Number of words highlighted + """ + + def highlight_regex( + self, + re_highlight: Union[str, Pattern[str]], + style: Optional[StyleType] = None, + *, + style_prefix: str = "", + ) -> int: + """ + Highlight text matching a regular expression. + + Args: + re_highlight: Regular expression pattern + style: Style to apply or None to use groups + style_prefix: Prefix for group-based style names + + Returns: + Number of matches highlighted + """ + + def append( + self, + text: Union[str, "Text"], + style: Optional[StyleType] = None, + ) -> "Text": + """ + Append text with optional styling. + + Args: + text: Text to append + style: Style to apply to appended text + + Returns: + Self for method chaining + """ + + def copy(self) -> "Text": + """ + Create a copy of this text object. + + Returns: + New Text instance with same content and styles + """ + + def copy_styles(self, text: "Text") -> None: + """ + Copy styles from another text object. + + Args: + text: Text object to copy styles from + """ + + def split( + self, + separator: str = "\n", + *, + include_separator: bool = False, + allow_blank: bool = True, + ) -> List["Text"]: + """ + Split text by separator. + + Args: + separator: String to split on + include_separator: Include separator in results + allow_blank: Allow blank strings in results + + Returns: + List of Text objects + """ + + def divide( + self, offsets: Iterable[int] + ) -> List["Text"]: + """ + Divide text at specific character offsets. + + Args: + offsets: Character positions to split at + + Returns: + List of Text objects + """ + + def right_crop(self, amount: int = 1) -> "Text": + """ + Remove characters from the right. + + Args: + amount: Number of characters to remove + + Returns: + New Text with characters removed + """ + + def truncate( + self, + max_width: int, + *, + overflow: OverflowMethod = "fold", + pad: bool = False, + ) -> None: + """ + Truncate text to maximum width. + + Args: + max_width: Maximum width in characters + overflow: Overflow handling method + pad: Pad to exact width with spaces + """ + + def pad_left(self, count: int, character: str = " ") -> "Text": + """ + Add padding to the left. + + Args: + count: Number of characters to add + character: Character to use for padding + + Returns: + New Text with padding + """ + + def pad_right(self, count: int, character: str = " ") -> "Text": + """ + Add padding to the right. + + Args: + count: Number of characters to add + character: Character to use for padding + + Returns: + New Text with padding + """ + + def align(self, align: AlignMethod, width: int, character: str = " ") -> "Text": + """ + Align text within a given width. + + Args: + align: Alignment method + width: Total width + character: Character to use for padding + + Returns: + New aligned Text + """ + + def strip(self, characters: Optional[str] = None) -> "Text": + """ + Strip characters from both ends. + + Args: + characters: Characters to strip or None for whitespace + + Returns: + New Text with characters stripped + """ + + def rstrip(self, characters: Optional[str] = None) -> "Text": + """ + Strip characters from the right end. + + Args: + characters: Characters to strip or None for whitespace + + Returns: + New Text with characters stripped + """ + + def lstrip(self, characters: Optional[str] = None) -> "Text": + """ + Strip characters from the left end. + + Args: + characters: Characters to strip or None for whitespace + + Returns: + New Text with characters stripped + """ + + def expand_tabs(self, tab_size: Optional[int] = None) -> "Text": + """ + Expand tab characters to spaces. + + Args: + tab_size: Size of tabs or None to use default + + Returns: + New Text with tabs expanded + """ + + def replace( + self, + old: str, + new: Union[str, "Text"], + count: int = -1, + ) -> "Text": + """ + Replace occurrences of a substring. + + Args: + old: Substring to replace + new: Replacement text + count: Maximum replacements or -1 for all + + Returns: + New Text with replacements + """ + + @classmethod + def from_markup( + cls, + text: str, + *, + style: StyleType = "", + emoji: bool = True, + emoji_variant: Optional[EmojiVariant] = None, + ) -> "Text": + """ + Create Text from Rich markup. + + Args: + text: Text with Rich markup tags + style: Base style to apply + emoji: Enable emoji rendering + emoji_variant: Emoji variant preference + + Returns: + New Text object with markup applied + """ + + @classmethod + def from_ansi( + cls, + text: str, + *, + style: StyleType = "", + no_color: Optional[bool] = None, + ) -> "Text": + """ + Create Text from ANSI escaped string. + + Args: + text: Text with ANSI escape codes + style: Base style to apply + no_color: Disable color processing + + Returns: + New Text object with ANSI styles converted + """ + + @classmethod + def styled( + cls, + text: str, + style: StyleType, + ) -> "Text": + """ + Create styled Text. + + Args: + text: Plain text + style: Style to apply + + Returns: + New styled Text object + """ + + @classmethod + def assemble( + cls, + *parts: Union[str, "Text", Tuple[str, StyleType]], + style: StyleType = "", + justify: Optional[JustifyMethod] = None, + overflow: OverflowMethod = "fold", + no_wrap: Optional[bool] = None, + end: str = "\n", + tab_size: int = 8, + meta: Optional[Dict[str, Any]] = None, + ) -> "Text": + """ + Assemble Text from multiple parts. + + Args: + *parts: Text parts as strings, Text objects, or (text, style) tuples + style: Base style + justify: Text justification + overflow: Overflow handling + no_wrap: Disable wrapping + end: End string + tab_size: Tab size + meta: Metadata dictionary + + Returns: + New assembled Text object + """ + + # Properties + @property + def plain(self) -> str: + """Get plain text without styling.""" + + @property + def markup(self) -> str: + """Get text as Rich markup.""" + + @property + def spans(self) -> List[Span]: + """Get list of styled spans.""" + + @property + def style(self) -> Style: + """Get base style.""" +``` + +### Span Class + +Represents a styled region within text. + +```python { .api } +class Span(NamedTuple): + """ + A styled span within text. + + Attributes: + start: Start character index + end: End character index + style: Style for this span + """ + start: int + end: int + style: Union[str, Style] + + def split(self, offset: int) -> Tuple["Span", Optional["Span"]]: + """ + Split span at offset. + + Args: + offset: Character offset to split at + + Returns: + Tuple of (left_span, right_span or None) + """ + + def move(self, offset: int) -> "Span": + """ + Move span by offset. + + Args: + offset: Number of characters to move + + Returns: + New span with adjusted position + """ + + def right_crop(self, offset: int) -> "Span": + """ + Crop span at offset. + + Args: + offset: Offset to crop at + + Returns: + New cropped span + """ +``` + +### Style Class + +Comprehensive styling system with support for colors, typography, and effects. + +```python { .api } +class Style: + """ + Immutable style definition for text formatting. + + Args: + color: Text color + bgcolor: Background color + bold: Bold text + dim: Dim text + italic: Italic text + underline: Underlined text + blink: Blinking text + blink2: Fast blinking text + reverse: Reverse video + conceal: Concealed text + strike: Strikethrough text + underline2: Double underline + frame: Framed text + encircle: Encircled text + overline: Overlined text + link: Hyperlink URL + meta: Metadata dictionary + """ + def __init__( + self, + *, + color: Optional[ColorType] = None, + bgcolor: Optional[ColorType] = None, + bold: Optional[bool] = None, + dim: Optional[bool] = None, + italic: Optional[bool] = None, + underline: Optional[bool] = None, + blink: Optional[bool] = None, + blink2: Optional[bool] = None, + reverse: Optional[bool] = None, + conceal: Optional[bool] = None, + strike: Optional[bool] = None, + underline2: Optional[bool] = None, + frame: Optional[bool] = None, + encircle: Optional[bool] = None, + overline: Optional[bool] = None, + link: Optional[str] = None, + meta: Optional[Dict[str, Any]] = None, + ): ... + + def copy(self) -> "Style": + """ + Create a copy of this style. + + Returns: + New Style with same properties + """ + + def update(self, **kwargs: Any) -> "Style": + """ + Update style properties. + + Args: + **kwargs: Style properties to update + + Returns: + New Style with updated properties + """ + + def render( + self, + text: str = "", + *, + color_system: ColorSystem = ColorSystem.TRUECOLOR, + legacy_windows: bool = False, + ) -> str: + """ + Render style as ANSI codes. + + Args: + text: Text to wrap with codes + color_system: Target color system + legacy_windows: Enable legacy Windows compatibility + + Returns: + Text with ANSI escape codes + """ + + def test(self, text: Optional[str] = None) -> "Text": + """ + Create test text with this style applied. + + Args: + text: Test text or None for default + + Returns: + Styled Text object for testing + """ + + def without_color(self) -> "Style": + """ + Create style without color information. + + Returns: + New Style with colors removed + """ + + @classmethod + def parse(cls, style: Union[str, "Style"]) -> "Style": + """ + Parse style from string or return existing Style. + + Args: + style: Style string or Style object + + Returns: + Parsed Style object + """ + + @classmethod + def combine(cls, styles: Iterable["Style"]) -> "Style": + """ + Combine multiple styles. + + Args: + styles: Iterable of styles to combine + + Returns: + New combined Style + """ + + @classmethod + def chain(cls, *styles: "Style") -> "Style": + """ + Chain multiple styles together. + + Args: + *styles: Styles to chain + + Returns: + New chained Style + """ + + @classmethod + def null(cls) -> "Style": + """ + Create a null style with no formatting. + + Returns: + Empty Style object + """ + + # Properties + @property + def color(self) -> Optional[Color]: + """Text color.""" + + @property + def bgcolor(self) -> Optional[Color]: + """Background color.""" + + @property + def bold(self) -> Optional[bool]: + """Bold flag.""" + + @property + def dim(self) -> Optional[bool]: + """Dim flag.""" + + @property + def italic(self) -> Optional[bool]: + """Italic flag.""" + + @property + def underline(self) -> Optional[bool]: + """Underline flag.""" + + @property + def strike(self) -> Optional[bool]: + """Strikethrough flag.""" + + @property + def link(self) -> Optional[str]: + """Hyperlink URL.""" + + @property + def transparent_background(self) -> bool: + """Check if background is transparent.""" +``` + +**Usage Examples:** + +```python +from rich.text import Text, Span +from rich.style import Style +from rich.console import Console + +console = Console() + +# Basic text creation +text = Text("Hello World!") +text.stylize("bold red", 0, 5) # Style "Hello" +text.stylize("italic blue", 6, 12) # Style "World!" +console.print(text) + +# Text from markup +markup_text = Text.from_markup("[bold red]Error:[/bold red] Something went wrong") +console.print(markup_text) + +# Text assembly +assembled = Text.assemble( + "Status: ", + ("OK", "bold green"), + " - ", + ("Ready", "italic blue") +) +console.print(assembled) + +# Text with highlighting +code_text = Text("def hello_world():") +code_text.highlight_words(["def", "hello_world"], "bold blue") +console.print(code_text) + +# Advanced styling +style = Style( + color="red", + bgcolor="yellow", + bold=True, + italic=True, + underline=True +) +styled_text = Text("Important Notice", style=style) +console.print(styled_text) + +# Text manipulation +long_text = Text("This is a very long line of text that needs to be processed") +long_text.truncate(20, overflow="ellipsis") +console.print(long_text) + +# Working with spans +text = Text("Hello World") +spans = [ + Span(0, 5, "bold red"), + Span(6, 11, "italic blue") +] +for span in spans: + text.stylize(span.style, span.start, span.end) +console.print(text) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/utilities.md b/.tessl/tiles/tessl/pypi-rich/docs/utilities.md new file mode 100644 index 000000000..2d551e01e --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/docs/utilities.md @@ -0,0 +1,74 @@ +# Utilities + +Color handling, measurement, error types, and other utility functions. Rich provides various utility functions and classes for working with colors, measurements, and other common operations. + +## Capabilities + +### Color System + +Comprehensive color handling and conversion. + +```python { .api } +class Color: + """Color representation with conversion capabilities.""" + + @classmethod + def parse(cls, color: ColorType) -> "Color": + """Parse color from various formats.""" + + def blend(self, destination: "Color", factor: float) -> "Color": + """Blend with another color.""" + + def get_truecolor(self) -> Tuple[int, int, int]: + """Get RGB values.""" + +class ColorTriplet(NamedTuple): + """RGB color triplet.""" + red: int + green: int + blue: int + +def inspect( + obj: Any, + *, + console: Optional[Console] = None, + title: Optional[str] = None, + help: bool = False, + methods: bool = False, + docs: bool = True, + private: bool = False, + dunder: bool = False, + sort: bool = True, + all: bool = False, + value: bool = True, +) -> None: + """Inspect Python objects with rich formatting.""" + +class Measurement(NamedTuple): + """Content measurement with min/max dimensions.""" + minimum: int + maximum: int + + def with_maximum(self, maximum: int) -> "Measurement": ... + def with_minimum(self, minimum: int) -> "Measurement": ... + def clamp(self, min_width: int, max_width: int) -> "Measurement": ... +``` + +**Usage Examples:** + +```python +from rich.color import Color +from rich import inspect +from rich.console import Console + +console = Console() + +# Color operations +red = Color.parse("red") +blue = Color.parse("#0000FF") +purple = red.blend(blue, 0.5) + +# Object inspection +inspect([1, 2, 3, 4, 5]) +inspect(console, methods=True) +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/tile.json b/.tessl/tiles/tessl/pypi-rich/tile.json new file mode 100644 index 000000000..963f8beee --- /dev/null +++ b/.tessl/tiles/tessl/pypi-rich/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-rich", + "version": "14.1.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/rich@14.1.0", + "summary": "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-ty/docs/index.md b/.tessl/tiles/tessl/pypi-ty/docs/index.md new file mode 100644 index 000000000..c9b757e10 --- /dev/null +++ b/.tessl/tiles/tessl/pypi-ty/docs/index.md @@ -0,0 +1,413 @@ +# ty + +An extremely fast Python type checker and language server written in Rust. ty provides comprehensive type checking for Python codebases with advanced error reporting, configuration options, and Language Server Protocol support for seamless editor integration. + +## Package Information + +- **Package Name**: ty +- **Package Type**: pypi +- **Language**: Python (with Rust backend) +- **Installation**: `pip install ty` or `uv tool install ty` or `pipx install ty` +- **Version**: 0.0.1-alpha.20 +- **License**: MIT +- **Repository**: https://github.com/astral-sh/ty +- **Documentation**: https://docs.astral.sh/ty/ + +## Core Imports + +The ty package can be imported as a Python module but provides minimal programmatic API: + +```python +import ty +``` + +However, ty is primarily used as a command-line tool: + +```bash +ty check +``` + +## Basic Usage + +```bash +# Basic type checking +ty check + +# Check specific files +ty check src/main.py tests/ + +# Watch mode with auto-recheck +ty check --watch + +# Use specific Python version +ty check --python-version 3.11 + +# Start language server +ty server +``` + +For project integration: + +```bash +# Add to project as dev dependency +uv add --dev ty + +# Run with uv +uv run ty check +``` + +## Architecture + +ty consists of several key components: + +- **Type Checker Core**: Rust-based high-performance type analysis engine +- **CLI Interface**: Command-line interface for type checking operations +- **Language Server**: LSP implementation for real-time editor integration +- **Configuration System**: Flexible TOML-based configuration for projects +- **Module Resolution**: Sophisticated Python package and module discovery +- **Binary Wrapper**: Python wrapper that locates and executes the Rust binary + +The architecture prioritizes performance through Rust implementation while maintaining compatibility with Python tooling and ecosystems. + +## Capabilities + +### Command Line Interface + +Core command-line functionality for type checking, language server operation, and tool management. + +```bash { .api } +# Main command +ty + +# Type checking +ty check [OPTIONS] [PATHS]... + +# Language server +ty server + +# Version information +ty version + +# Shell completion +ty generate-shell-completion + +# Help +ty help [COMMAND] +``` + +**Arguments:** +- `PATHS`: List of files or directories to check (optional, defaults to project root) +- `SHELL`: Shell type for completion generation (bash, zsh, fish, powershell, elvish) +- `COMMAND`: Subcommand to get help for (optional) + +**Global Options for `ty check`:** + +File and path options: +- `--project `: Run within specific project directory +- `--exclude `: Glob patterns to exclude from checking + +Python environment options: +- `--python `: Path to Python environment or interpreter. ty uses this to resolve type information and third-party dependencies. If not specified, ty attempts to infer from VIRTUAL_ENV/CONDA_PREFIX or discover .venv directory. If interpreter path is provided (e.g., .venv/bin/python3), ty attempts to find environment two directories up. Does not invoke interpreter, so won't resolve dynamic executables like shims. +- `--python-version `, `--target-version `: Target Python version (3.7-3.13). If not specified, ty tries: 1) project.requires-python in pyproject.toml, 2) infer from Python environment, 3) fallback to 3.13 +- `--python-platform `, `--platform `: Target platform (win32/darwin/linux/android/ios/all). Used to specialize sys.platform type. If 'all', no platform assumptions are made. Defaults to current system platform. +- `--extra-search-path `: Additional module resolution paths (can be repeated) +- `--typeshed `, `--custom-typeshed-dir `: Custom typeshed directory for standard library types + +Configuration options: +- `--config-file `: Path to ty.toml configuration file +- `--config `, `-c `: TOML key=value configuration override + +Rule control options: +- `--error `: Treat rule as error-level (can be repeated) +- `--warn `: Treat rule as warning-level (can be repeated) +- `--ignore `: Disable rule (can be repeated) + +Output options: +- `--output-format `: Message format (full/concise) +- `--color `: Color output control (auto/always/never) +- `--quiet/-q`: Quiet output (use -qq for silent) +- `--verbose/-v`: Verbose output (use -vv, -vvv for more verbose) +- `--help/-h`: Print help information + +Behavior options: +- `--watch/-W`: Watch mode for continuous checking +- `--error-on-warning`: Exit code 1 on warnings +- `--exit-zero`: Always exit with code 0 +- `--respect-ignore-files`: Respect file exclusions via .gitignore and other standard ignore files (use --no-respect-gitignore to disable) + +### Python Module API + +Limited programmatic access through Python module wrapper. + +```python { .api } +def find_ty_bin() -> str: + """ + Return the ty binary path. + + Searches various installation locations including: + - System scripts directory + - User-specific installation paths + - Virtual environment paths + - pip build environments + - Adjacent bin directories + + Returns: + str: Absolute path to ty binary executable + + Raises: + FileNotFoundError: If ty binary cannot be located + """ +``` + +**Usage Example:** +```python +from ty import find_ty_bin +import subprocess + +# Get ty binary path +ty_path = find_ty_bin() + +# Run type checking programmatically +result = subprocess.run([ty_path, "check", "src/"], capture_output=True, text=True) +print(result.stdout) +``` + +### Configuration System + +Comprehensive TOML-based configuration supporting project-level and user-level settings. + +```toml { .api } +# pyproject.toml format +[tool.ty.rules] +rule_name = "ignore" | "warn" | "error" + +[tool.ty.environment] +extra-paths = ["./shared/my-search-path"] +python = "./.venv" +python-platform = "win32" | "darwin" | "android" | "ios" | "linux" | "all" +python-version = "3.7" | "3.8" | "3.9" | "3.10" | "3.11" | "3.12" | "3.13" +root = ["./src", "./lib"] +typeshed = "/path/to/custom/typeshed" + +[tool.ty.src] +include = ["src", "tests"] +exclude = ["generated", "*.proto", "tests/fixtures/**"] # Additional excludes beyond defaults +respect-ignore-files = true + +[tool.ty.terminal] +output-format = "full" | "concise" +error-on-warning = false + +[[tool.ty.overrides]] +include = ["tests/**"] +exclude = ["tests/fixtures/**"] + +[tool.ty.overrides.rules] +rule_name = "ignore" | "warn" | "error" +``` + +**Configuration File Locations:** +- Project: `pyproject.toml` (under `[tool.ty]`) or `ty.toml` +- User: `~/.config/ty/ty.toml` or `$XDG_CONFIG_HOME/ty/ty.toml` (Linux/macOS) +- User: `%APPDATA%\ty\ty.toml` (Windows) + +**Precedence:** Command line > project config > user config + +### Language Server Protocol + +Language server implementation providing real-time type checking and editor integration. + +```bash { .api } +# Start language server +ty server +``` + +**LSP Features:** +- Real-time type checking as you type +- Diagnostic reporting with error/warning details +- Hover information for type details +- Go-to-definition support +- Symbol search and workspace analysis +- Configuration synchronization + +**Editor Integration:** + +VS Code: +```json +{ + "ty.enable": true, + "ty.path": "/path/to/ty", + "ty.settings": { + "python-version": "3.11" + } +} +``` + +Neovim (nvim-lspconfig): +```lua +require('lspconfig').ty.setup({ + settings = { + ty = { + -- ty language server settings + } + } +}) +``` + +Neovim 0.11+ (vim.lsp.config): +```lua +vim.lsp.config('ty', { + settings = { + ty = { + -- ty language server settings + } + } +}) +vim.lsp.enable('ty') +``` + +### Environment Variables + +Environment variables that control ty's behavior and configuration. + +```bash { .api } +# Configuration file path +export TY_CONFIG_FILE="/path/to/ty.toml" + +# Python environment detection +export VIRTUAL_ENV="/path/to/venv" +export CONDA_PREFIX="/path/to/conda/env" +``` + +**Environment Variables:** +- `TY_CONFIG_FILE`: Override configuration file location +- `VIRTUAL_ENV`: Active virtual environment path (auto-detected) +- `CONDA_PREFIX`: Conda environment path (auto-detected) + +### Exit Codes + +ty uses standard exit codes to indicate the result of operations. + +```bash { .api } +# Exit codes +0 # Success (no errors, or only warnings without --error-on-warning) +1 # Type errors found, or warnings with --error-on-warning +2 # Configuration or runtime errors +``` + +## Module Discovery + +ty implements sophisticated module resolution for Python projects: + +### First-party Modules +- Searches project root and `src/` directories by default +- Configurable via `environment.root` setting +- Supports src-layout, flat-layout, and custom project structures +- Automatically includes `tests/` if it's not a package + +### Third-party Modules +- Resolves from configured Python environment +- Searches `site-packages` directories +- Supports virtual environments and conda environments +- Uses `--python` or auto-discovery via `VIRTUAL_ENV`/`CONDA_PREFIX` + +### Standard Library +- Uses built-in typeshed stubs (bundled as zip in binary) +- Version-specific based on target Python version +- Customizable via `--typeshed` option + +## Rule System + +ty provides a comprehensive rule system for type checking configuration: + +### Rule Categories +- Type compatibility and assignment checking +- Import resolution and missing import detection +- Attribute access validation +- Function call signature validation +- Variable binding and scope analysis + +### Rule Configuration +```toml +[tool.ty.rules] +# Examples of common rules +possibly-unresolved-reference = "warn" +division-by-zero = "ignore" +index-out-of-bounds = "error" +redundant-cast = "ignore" +unused-ignore-comment = "warn" +``` + +### Rule Overrides +```toml +# File-specific rule overrides +[[tool.ty.overrides]] +include = ["tests/**", "**/test_*.py"] + +[tool.ty.overrides.rules] +possibly-unresolved-reference = "ignore" # Relax for tests +``` + +## Performance Features + +ty is optimized for speed and efficiency: + +- **Rust Implementation**: Core type checker written in Rust for maximum performance +- **Incremental Checking**: Only re-analyzes changed files and dependencies +- **Watch Mode**: Continuous checking with file system monitoring +- **Fast Startup**: Minimal initialization overhead +- **Memory Efficient**: Optimized memory usage for large codebases +- **Parallel Analysis**: Multi-threaded processing where possible + +## Integration Patterns + +### CI/CD Integration +```yaml +# GitHub Actions example +- name: Type check with ty + run: | + uv tool install ty + ty check --error-on-warning +``` + +### Pre-commit Hook +```yaml +# .pre-commit-config.yaml +repos: + - repo: https://github.com/astral-sh/ty + rev: v0.0.1-alpha.20 + hooks: + - id: ty +``` + +### Development Workflow +```bash +# Development commands +ty check --watch # Continuous checking +ty check --verbose # Detailed output +ty check src/ tests/ # Specific paths +ty check --python .venv # Specific environment +``` + +### Editor Integration +- **VS Code**: Official extension (astral-sh.ty) +- **Neovim**: Built-in LSP support +- **Vim**: LSP client configuration +- **Emacs**: lsp-mode integration +- **Any LSP-compatible editor**: Generic LSP server support + +## Types + +```python { .api } +# Configuration types (for reference) +RuleName = str # Rule identifier (e.g., "possibly-unresolved-reference") +Severity = "ignore" | "warn" | "error" +PythonVersion = "3.7" | "3.8" | "3.9" | "3.10" | "3.11" | "3.12" | "3.13" +Platform = "win32" | "darwin" | "android" | "ios" | "linux" | "all" +OutputFormat = "full" | "concise" +ColorOption = "auto" | "always" | "never" + +# Path specifications +Path = str # File or directory path +GlobPattern = str # Gitignore-style pattern +``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-ty/tile.json b/.tessl/tiles/tessl/pypi-ty/tile.json new file mode 100644 index 000000000..c9b0da92b --- /dev/null +++ b/.tessl/tiles/tessl/pypi-ty/tile.json @@ -0,0 +1,7 @@ +{ + "name": "tessl/pypi-ty", + "version": "0.0.0", + "docs": "docs/index.md", + "describes": "pkg:pypi/ty@0.0.1-alpha.20", + "summary": "An extremely fast Python type checker and language server, written in Rust." +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 289db1b15..d0165d8d4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -318,3 +318,6 @@ When building a new isolated environment (e.g., for a new cloud provider): See `rlm/environments/modal_repl.py` as the canonical reference implementation. +# Agent Rules + +@.tessl/RULES.md follow the [instructions](.tessl/RULES.md) diff --git a/README.md b/README.md index 75dc14b10..e59c5ff1e 100644 --- a/README.md +++ b/README.md @@ -158,3 +158,4 @@ You'll have the option to select saved `.jsonl` files

RLM Visualizer Example

+ diff --git a/docs/package-lock.json b/docs/package-lock.json index 531c60163..f83b0fa9f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -13,7 +13,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "lucide-react": "^0.294.0", - "next": "^14.0.0", + "next": "16.1.5", "react": "^18.2.0", "react-dom": "^18.2.0", "tailwind-merge": "^2.0.0" @@ -41,6 +41,456 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -81,19 +531,17 @@ } }, "node_modules/@next/env": { - "version": "14.2.35", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", - "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==", - "license": "MIT" + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.5.tgz", + "integrity": "sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", - "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.5.tgz", + "integrity": "sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -103,13 +551,12 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", - "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.5.tgz", + "integrity": "sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -119,13 +566,12 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", - "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.5.tgz", + "integrity": "sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -135,13 +581,12 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", - "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.5.tgz", + "integrity": "sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -151,13 +596,12 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", - "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.5.tgz", + "integrity": "sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -167,13 +611,12 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", - "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.5.tgz", + "integrity": "sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -183,29 +626,12 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", - "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.5.tgz", + "integrity": "sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==", "cpu": [ "arm64" ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", - "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", - "cpu": [ - "ia32" - ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -215,13 +641,12 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.33", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", - "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.5.tgz", + "integrity": "sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -593,20 +1018,12 @@ } } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, "node_modules/@swc/helpers": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", - "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "license": "Apache-2.0", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dependencies": { - "@swc/counter": "^0.1.3", - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, "node_modules/@types/node": { @@ -716,7 +1133,6 @@ "version": "2.9.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", - "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -782,17 +1198,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -876,8 +1281,7 @@ "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, "node_modules/clsx": { "version": "2.1.1", @@ -918,6 +1322,15 @@ "devOptional": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -1054,12 +1467,6 @@ "node": ">=10.13.0" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1247,41 +1654,40 @@ } }, "node_modules/next": { - "version": "14.2.35", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", - "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", - "license": "MIT", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.5.tgz", + "integrity": "sha512-f+wE+NSbiQgh3DSAlTaw2FwY5yGdVViAtp8TotNQj4kk4Q8Bh1sC/aL9aH+Rg1YAVn18OYXsRDT7U/079jgP7w==", "dependencies": { - "@next/env": "14.2.35", - "@swc/helpers": "0.5.5", - "busboy": "1.6.0", + "@next/env": "16.1.5", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.6" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.33", - "@next/swc-darwin-x64": "14.2.33", - "@next/swc-linux-arm64-gnu": "14.2.33", - "@next/swc-linux-arm64-musl": "14.2.33", - "@next/swc-linux-x64-gnu": "14.2.33", - "@next/swc-linux-x64-musl": "14.2.33", - "@next/swc-win32-arm64-msvc": "14.2.33", - "@next/swc-win32-ia32-msvc": "14.2.33", - "@next/swc-win32-x64-msvc": "14.2.33" + "@next/swc-darwin-arm64": "16.1.5", + "@next/swc-darwin-x64": "16.1.5", + "@next/swc-linux-arm64-gnu": "16.1.5", + "@next/swc-linux-arm64-musl": "16.1.5", + "@next/swc-linux-x64-gnu": "16.1.5", + "@next/swc-linux-x64-musl": "16.1.5", + "@next/swc-win32-arm64-msvc": "16.1.5", + "@next/swc-win32-x64-msvc": "16.1.5", + "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "peerDependenciesMeta": { @@ -1291,6 +1697,9 @@ "@playwright/test": { "optional": true }, + "babel-plugin-react-compiler": { + "optional": true + }, "sass": { "optional": true } @@ -1704,6 +2113,62 @@ "loose-envify": "^1.1.0" } }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1713,19 +2178,10 @@ "node": ">=0.10.0" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "license": "MIT", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "dependencies": { "client-only": "0.0.1" }, @@ -1733,7 +2189,7 @@ "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { @@ -1922,8 +2378,7 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/typescript": { "version": "5.9.3", diff --git a/docs/package.json b/docs/package.json index ff5ef87a4..a7b8e467f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "next": "^14.0.0", + "next": "16.1.5", "react": "^18.2.0", "react-dom": "^18.2.0", "class-variance-authority": "^0.7.0", diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..65bec4b8a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "rlm", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/pyproject.toml b/pyproject.toml index 0b331237e..29842b260 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ readme = "README.md" requires-python = ">=3.11" dependencies = [ "anthropic>=0.75.0", - "google-genai>=1.56.0", + "google-genai>=1.0.0", "openai>=2.14.0", "portkey-ai>=2.1.0", "pytest>=9.0.2", @@ -64,3 +64,4 @@ quote-style = "double" [tool.pytest.ini_options] testpaths = ["tests"] + diff --git a/rlm/clients/gemini.py b/rlm/clients/gemini.py index 7f6dc1520..265aaa2ca 100644 --- a/rlm/clients/gemini.py +++ b/rlm/clients/gemini.py @@ -17,7 +17,7 @@ class GeminiClient(BaseLM): """ LM Client for running models with the Google Gemini API. - Uses the official google-genai SDK. + Uses the new google-genai SDK (v1.0+). """ def __init__( @@ -52,8 +52,8 @@ def __init__( def completion(self, prompt: str | list[dict[str, Any]], model: str | None = None) -> str: contents, system_instruction = self._prepare_contents(prompt) - model = model or self.model_name - if not model: + model_name = model or self.model_name + if not model_name: raise ValueError("Model name is required for Gemini client.") config = None @@ -61,12 +61,10 @@ def completion(self, prompt: str | list[dict[str, Any]], model: str | None = Non config = types.GenerateContentConfig(system_instruction=system_instruction) response = self.client.models.generate_content( - model=model, - contents=contents, - config=config, + model=model_name, contents=contents, config=config ) - self._track_cost(response, model) + self._track_cost(response, model_name) return response.text async def acompletion( @@ -74,27 +72,24 @@ async def acompletion( ) -> str: contents, system_instruction = self._prepare_contents(prompt) - model = model or self.model_name - if not model: + model_name = model or self.model_name + if not model_name: raise ValueError("Model name is required for Gemini client.") config = None if system_instruction: config = types.GenerateContentConfig(system_instruction=system_instruction) - # google-genai SDK supports async via aio interface response = await self.client.aio.models.generate_content( - model=model, - contents=contents, - config=config, + model=model_name, contents=contents, config=config ) - self._track_cost(response, model) + self._track_cost(response, model_name) return response.text def _prepare_contents( self, prompt: str | list[dict[str, Any]] - ) -> tuple[list[types.Content] | str, str | None]: + ) -> tuple[list[dict[str, Any]] | str, str | None]: """Prepare contents and extract system instruction for Gemini API.""" system_instruction = None @@ -112,19 +107,19 @@ def _prepare_contents( # Gemini handles system instruction separately system_instruction = content elif role == "user": - contents.append(types.Content(role="user", parts=[types.Part(text=content)])) + contents.append({"role": "user", "parts": [{"text": content}]}) elif role == "assistant": # Gemini uses "model" instead of "assistant" - contents.append(types.Content(role="model", parts=[types.Part(text=content)])) + contents.append({"role": "model", "parts": [{"text": content}]}) else: # Default to user role for unknown roles - contents.append(types.Content(role="user", parts=[types.Part(text=content)])) + contents.append({"role": "user", "parts": [{"text": content}]}) return contents, system_instruction raise ValueError(f"Invalid prompt type: {type(prompt)}") - def _track_cost(self, response: types.GenerateContentResponse, model: str): + def _track_cost(self, response: Any, model: str): self.model_call_counts[model] += 1 # Extract token usage from response diff --git a/tests/clients/test_gemini.py b/tests/clients/test_gemini.py index 181a3ee8d..0bf4cca7f 100644 --- a/tests/clients/test_gemini.py +++ b/tests/clients/test_gemini.py @@ -12,128 +12,119 @@ load_dotenv() +@patch("rlm.clients.gemini.genai.Client") class TestGeminiClientUnit: """Unit tests that don't require API calls.""" - def test_init_with_api_key(self): + def test_init_with_api_key(self, mock_client_class): """Test client initialization with explicit API key.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key", model_name="gemini-2.5-flash") - assert client.model_name == "gemini-2.5-flash" + client = GeminiClient(api_key="test-key", model_name="gemini-2.5-flash") + mock_client_class.assert_called_with(api_key="test-key") + assert client.model_name == "gemini-2.5-flash" - def test_init_default_model(self): + def test_init_default_model(self, mock_client_class): """Test client uses default model name.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - assert client.model_name == "gemini-2.5-flash" + client = GeminiClient(api_key="test-key") + assert client.model_name == "gemini-2.5-flash" - def test_init_requires_api_key(self): + def test_init_requires_api_key(self, mock_client_class): """Test client raises error when no API key provided.""" with patch.dict(os.environ, {}, clear=True): with patch("rlm.clients.gemini.DEFAULT_GEMINI_API_KEY", None): with pytest.raises(ValueError, match="Gemini API key is required"): GeminiClient(api_key=None) - def test_usage_tracking_initialization(self): + def test_usage_tracking_initialization(self, mock_client_class): """Test that usage tracking is properly initialized.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - assert client.model_call_counts == {} - assert client.model_input_tokens == {} - assert client.model_output_tokens == {} - assert client.last_prompt_tokens == 0 - assert client.last_completion_tokens == 0 - - def test_get_usage_summary_empty(self): + client = GeminiClient(api_key="test-key") + assert client.model_call_counts == {} + assert client.model_input_tokens == {} + assert client.model_output_tokens == {} + assert client.last_prompt_tokens == 0 + assert client.last_completion_tokens == 0 + + def test_get_usage_summary_empty(self, mock_client_class): """Test usage summary when no calls have been made.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - summary = client.get_usage_summary() - assert isinstance(summary, UsageSummary) - assert summary.model_usage_summaries == {} + client = GeminiClient(api_key="test-key") + summary = client.get_usage_summary() + assert isinstance(summary, UsageSummary) + assert summary.model_usage_summaries == {} - def test_get_last_usage(self): + def test_get_last_usage(self, mock_client_class): """Test last usage returns correct format.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - client.last_prompt_tokens = 100 - client.last_completion_tokens = 50 - usage = client.get_last_usage() - assert isinstance(usage, ModelUsageSummary) - assert usage.total_calls == 1 - assert usage.total_input_tokens == 100 - assert usage.total_output_tokens == 50 - - def test_prepare_contents_string(self): + client = GeminiClient(api_key="test-key") + client.last_prompt_tokens = 100 + client.last_completion_tokens = 50 + usage = client.get_last_usage() + assert isinstance(usage, ModelUsageSummary) + assert usage.total_calls == 1 + assert usage.total_input_tokens == 100 + assert usage.total_output_tokens == 50 + + def test_prepare_contents_string(self, mock_client_class): """Test _prepare_contents with string input.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - contents, system = client._prepare_contents("Hello world") - assert contents == "Hello world" - assert system is None + client = GeminiClient(api_key="test-key") + contents, system = client._prepare_contents("Hello world") + assert contents == "Hello world" + assert system is None - def test_prepare_contents_messages_with_system(self): + def test_prepare_contents_messages_with_system(self, mock_client_class): """Test _prepare_contents extracts system message.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - messages = [ - {"role": "system", "content": "You are helpful"}, - {"role": "user", "content": "Hello"}, - ] - contents, system = client._prepare_contents(messages) - assert system == "You are helpful" - assert len(contents) == 1 - assert contents[0].role == "user" - - def test_prepare_contents_role_mapping(self): + client = GeminiClient(api_key="test-key") + messages = [ + {"role": "system", "content": "You are helpful"}, + {"role": "user", "content": "Hello"}, + ] + contents, system = client._prepare_contents(messages) + assert system == "You are helpful" + assert len(contents) == 1 + assert contents[0]["role"] == "user" + assert contents[0]["parts"][0]["text"] == "Hello" + + def test_prepare_contents_role_mapping(self, mock_client_class): """Test _prepare_contents maps assistant to model.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - messages = [ - {"role": "user", "content": "Hello"}, - {"role": "assistant", "content": "Hi there"}, - {"role": "user", "content": "How are you?"}, - ] - contents, system = client._prepare_contents(messages) - assert system is None - assert len(contents) == 3 - assert contents[0].role == "user" - assert contents[1].role == "model" # assistant -> model - assert contents[2].role == "user" - - def test_prepare_contents_invalid_type(self): + client = GeminiClient(api_key="test-key") + messages = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there"}, + {"role": "user", "content": "How are you?"}, + ] + contents, system = client._prepare_contents(messages) + assert system is None + assert len(contents) == 3 + assert contents[0]["role"] == "user" + assert contents[1]["role"] == "model" + assert contents[2]["role"] == "user" + + def test_prepare_contents_invalid_type(self, mock_client_class): """Test _prepare_contents raises on invalid input.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key") - with pytest.raises(ValueError, match="Invalid prompt type"): - client._prepare_contents(12345) + client = GeminiClient(api_key="test-key") + with pytest.raises(ValueError, match="Invalid prompt type"): + client._prepare_contents(12345) - def test_completion_requires_model(self): + def test_completion_requires_model(self, mock_client_class): """Test completion raises when no model specified.""" - with patch("rlm.clients.gemini.genai.Client"): - client = GeminiClient(api_key="test-key", model_name=None) - with pytest.raises(ValueError, match="Model name is required"): - client.completion("Hello") + client = GeminiClient(api_key="test-key", model_name=None) + with pytest.raises(ValueError, match="Model name is required"): + client.completion("Hello") - def test_completion_with_mocked_response(self): + def test_completion_with_mocked_response(self, mock_client_class): """Test completion with mocked API response.""" + mock_client = mock_client_class.return_value mock_response = MagicMock() mock_response.text = "Hello from Gemini!" mock_response.usage_metadata.prompt_token_count = 10 mock_response.usage_metadata.candidates_token_count = 5 - with patch("rlm.clients.gemini.genai.Client") as mock_client_class: - mock_client = MagicMock() - mock_client.models.generate_content.return_value = mock_response - mock_client_class.return_value = mock_client + mock_client.models.generate_content.return_value = mock_response - client = GeminiClient(api_key="test-key", model_name="gemini-2.5-flash") - result = client.completion("Hello") + client = GeminiClient(api_key="test-key", model_name="gemini-2.5-flash") + result = client.completion("Hello") - assert result == "Hello from Gemini!" - assert client.model_call_counts["gemini-2.5-flash"] == 1 - assert client.model_input_tokens["gemini-2.5-flash"] == 10 - assert client.model_output_tokens["gemini-2.5-flash"] == 5 + assert result == "Hello from Gemini!" + assert client.model_call_counts["gemini-2.5-flash"] == 1 + assert client.model_input_tokens["gemini-2.5-flash"] == 10 + assert client.model_output_tokens["gemini-2.5-flash"] == 5 class TestGeminiClientIntegration: @@ -172,18 +163,9 @@ def test_message_list_completion(self): not os.environ.get("GEMINI_API_KEY"), reason="GEMINI_API_KEY not set", ) + @pytest.mark.asyncio async def test_async_completion(self): """Test async completion.""" client = GeminiClient(model_name="gemini-2.5-flash") result = await client.acompletion("What is 3+3? Reply with just the number.") assert "6" in result - - -if __name__ == "__main__": - # Run integration tests directly - test = TestGeminiClientIntegration() - print("Testing simple completion...") - test.test_simple_completion() - print("Testing message list completion...") - test.test_message_list_completion() - print("All integration tests passed!") diff --git a/uv.lock b/uv.lock deleted file mode 100644 index 653d14c3a..000000000 --- a/uv.lock +++ /dev/null @@ -1,2368 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.11" - -[[package]] -name = "aiofiles" -version = "24.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" }, -] - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, -] - -[[package]] -name = "aiohttp" -version = "3.13.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" }, - { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" }, - { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" }, - { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" }, - { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" }, - { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" }, - { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" }, - { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" }, - { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" }, - { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" }, - { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" }, - { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" }, - { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" }, - { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" }, - { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" }, - { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" }, - { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" }, - { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" }, - { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" }, - { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" }, - { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" }, - { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" }, - { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" }, - { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" }, - { url = "https://files.pythonhosted.org/packages/bf/78/7e90ca79e5aa39f9694dcfd74f4720782d3c6828113bb1f3197f7e7c4a56/aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be", size = 732139, upload-time = "2025-10-28T20:57:02.455Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/1f59215ab6853fbaa5c8495fa6cbc39edfc93553426152b75d82a5f32b76/aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742", size = 490082, upload-time = "2025-10-28T20:57:04.784Z" }, - { url = "https://files.pythonhosted.org/packages/68/7b/fe0fe0f5e05e13629d893c760465173a15ad0039c0a5b0d0040995c8075e/aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293", size = 489035, upload-time = "2025-10-28T20:57:06.894Z" }, - { url = "https://files.pythonhosted.org/packages/d2/04/db5279e38471b7ac801d7d36a57d1230feeee130bbe2a74f72731b23c2b1/aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811", size = 1720387, upload-time = "2025-10-28T20:57:08.685Z" }, - { url = "https://files.pythonhosted.org/packages/31/07/8ea4326bd7dae2bd59828f69d7fdc6e04523caa55e4a70f4a8725a7e4ed2/aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a", size = 1688314, upload-time = "2025-10-28T20:57:10.693Z" }, - { url = "https://files.pythonhosted.org/packages/48/ab/3d98007b5b87ffd519d065225438cc3b668b2f245572a8cb53da5dd2b1bc/aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4", size = 1756317, upload-time = "2025-10-28T20:57:12.563Z" }, - { url = "https://files.pythonhosted.org/packages/97/3d/801ca172b3d857fafb7b50c7c03f91b72b867a13abca982ed6b3081774ef/aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a", size = 1858539, upload-time = "2025-10-28T20:57:14.623Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0d/4764669bdf47bd472899b3d3db91fffbe925c8e3038ec591a2fd2ad6a14d/aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e", size = 1739597, upload-time = "2025-10-28T20:57:16.399Z" }, - { url = "https://files.pythonhosted.org/packages/c4/52/7bd3c6693da58ba16e657eb904a5b6decfc48ecd06e9ac098591653b1566/aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb", size = 1555006, upload-time = "2025-10-28T20:57:18.288Z" }, - { url = "https://files.pythonhosted.org/packages/48/30/9586667acec5993b6f41d2ebcf96e97a1255a85f62f3c653110a5de4d346/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded", size = 1683220, upload-time = "2025-10-28T20:57:20.241Z" }, - { url = "https://files.pythonhosted.org/packages/71/01/3afe4c96854cfd7b30d78333852e8e851dceaec1c40fd00fec90c6402dd2/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b", size = 1712570, upload-time = "2025-10-28T20:57:22.253Z" }, - { url = "https://files.pythonhosted.org/packages/11/2c/22799d8e720f4697a9e66fd9c02479e40a49de3de2f0bbe7f9f78a987808/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8", size = 1733407, upload-time = "2025-10-28T20:57:24.37Z" }, - { url = "https://files.pythonhosted.org/packages/34/cb/90f15dd029f07cebbd91f8238a8b363978b530cd128488085b5703683594/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04", size = 1550093, upload-time = "2025-10-28T20:57:26.257Z" }, - { url = "https://files.pythonhosted.org/packages/69/46/12dce9be9d3303ecbf4d30ad45a7683dc63d90733c2d9fe512be6716cd40/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476", size = 1758084, upload-time = "2025-10-28T20:57:28.349Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c8/0932b558da0c302ffd639fc6362a313b98fdf235dc417bc2493da8394df7/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23", size = 1716987, upload-time = "2025-10-28T20:57:30.233Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8b/f5bd1a75003daed099baec373aed678f2e9b34f2ad40d85baa1368556396/aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254", size = 425859, upload-time = "2025-10-28T20:57:32.105Z" }, - { url = "https://files.pythonhosted.org/packages/5d/28/a8a9fc6957b2cee8902414e41816b5ab5536ecf43c3b1843c10e82c559b2/aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a", size = 452192, upload-time = "2025-10-28T20:57:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/9b/36/e2abae1bd815f01c957cbf7be817b3043304e1c87bad526292a0410fdcf9/aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b", size = 735234, upload-time = "2025-10-28T20:57:36.415Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e3/1ee62dde9b335e4ed41db6bba02613295a0d5b41f74a783c142745a12763/aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61", size = 490733, upload-time = "2025-10-28T20:57:38.205Z" }, - { url = "https://files.pythonhosted.org/packages/1a/aa/7a451b1d6a04e8d15a362af3e9b897de71d86feac3babf8894545d08d537/aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4", size = 491303, upload-time = "2025-10-28T20:57:40.122Z" }, - { url = "https://files.pythonhosted.org/packages/57/1e/209958dbb9b01174870f6a7538cd1f3f28274fdbc88a750c238e2c456295/aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b", size = 1717965, upload-time = "2025-10-28T20:57:42.28Z" }, - { url = "https://files.pythonhosted.org/packages/08/aa/6a01848d6432f241416bc4866cae8dc03f05a5a884d2311280f6a09c73d6/aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694", size = 1667221, upload-time = "2025-10-28T20:57:44.869Z" }, - { url = "https://files.pythonhosted.org/packages/87/4f/36c1992432d31bbc789fa0b93c768d2e9047ec8c7177e5cd84ea85155f36/aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906", size = 1757178, upload-time = "2025-10-28T20:57:47.216Z" }, - { url = "https://files.pythonhosted.org/packages/ac/b4/8e940dfb03b7e0f68a82b88fd182b9be0a65cb3f35612fe38c038c3112cf/aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9", size = 1838001, upload-time = "2025-10-28T20:57:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ef/39f3448795499c440ab66084a9db7d20ca7662e94305f175a80f5b7e0072/aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011", size = 1716325, upload-time = "2025-10-28T20:57:51.327Z" }, - { url = "https://files.pythonhosted.org/packages/d7/51/b311500ffc860b181c05d91c59a1313bdd05c82960fdd4035a15740d431e/aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6", size = 1547978, upload-time = "2025-10-28T20:57:53.554Z" }, - { url = "https://files.pythonhosted.org/packages/31/64/b9d733296ef79815226dab8c586ff9e3df41c6aff2e16c06697b2d2e6775/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213", size = 1682042, upload-time = "2025-10-28T20:57:55.617Z" }, - { url = "https://files.pythonhosted.org/packages/3f/30/43d3e0f9d6473a6db7d472104c4eff4417b1e9df01774cb930338806d36b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49", size = 1680085, upload-time = "2025-10-28T20:57:57.59Z" }, - { url = "https://files.pythonhosted.org/packages/16/51/c709f352c911b1864cfd1087577760ced64b3e5bee2aa88b8c0c8e2e4972/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae", size = 1728238, upload-time = "2025-10-28T20:57:59.525Z" }, - { url = "https://files.pythonhosted.org/packages/19/e2/19bd4c547092b773caeb48ff5ae4b1ae86756a0ee76c16727fcfd281404b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa", size = 1544395, upload-time = "2025-10-28T20:58:01.914Z" }, - { url = "https://files.pythonhosted.org/packages/cf/87/860f2803b27dfc5ed7be532832a3498e4919da61299b4a1f8eb89b8ff44d/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4", size = 1742965, upload-time = "2025-10-28T20:58:03.972Z" }, - { url = "https://files.pythonhosted.org/packages/67/7f/db2fc7618925e8c7a601094d5cbe539f732df4fb570740be88ed9e40e99a/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a", size = 1697585, upload-time = "2025-10-28T20:58:06.189Z" }, - { url = "https://files.pythonhosted.org/packages/0c/07/9127916cb09bb38284db5036036042b7b2c514c8ebaeee79da550c43a6d6/aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940", size = 431621, upload-time = "2025-10-28T20:58:08.636Z" }, - { url = "https://files.pythonhosted.org/packages/fb/41/554a8a380df6d3a2bba8a7726429a23f4ac62aaf38de43bb6d6cde7b4d4d/aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4", size = 457627, upload-time = "2025-10-28T20:58:11Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8e/3824ef98c039d3951cb65b9205a96dd2b20f22241ee17d89c5701557c826/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673", size = 767360, upload-time = "2025-10-28T20:58:13.358Z" }, - { url = "https://files.pythonhosted.org/packages/a4/0f/6a03e3fc7595421274fa34122c973bde2d89344f8a881b728fa8c774e4f1/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd", size = 504616, upload-time = "2025-10-28T20:58:15.339Z" }, - { url = "https://files.pythonhosted.org/packages/c6/aa/ed341b670f1bc8a6f2c6a718353d13b9546e2cef3544f573c6a1ff0da711/aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3", size = 509131, upload-time = "2025-10-28T20:58:17.693Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f0/c68dac234189dae5c4bbccc0f96ce0cc16b76632cfc3a08fff180045cfa4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf", size = 1864168, upload-time = "2025-10-28T20:58:20.113Z" }, - { url = "https://files.pythonhosted.org/packages/8f/65/75a9a76db8364b5d0e52a0c20eabc5d52297385d9af9c35335b924fafdee/aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e", size = 1719200, upload-time = "2025-10-28T20:58:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/f5/55/8df2ed78d7f41d232f6bd3ff866b6f617026551aa1d07e2f03458f964575/aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5", size = 1843497, upload-time = "2025-10-28T20:58:24.672Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e0/94d7215e405c5a02ccb6a35c7a3a6cfff242f457a00196496935f700cde5/aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad", size = 1935703, upload-time = "2025-10-28T20:58:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/0b/78/1eeb63c3f9b2d1015a4c02788fb543141aad0a03ae3f7a7b669b2483f8d4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e", size = 1792738, upload-time = "2025-10-28T20:58:29.787Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/aaf1eea4c188e51538c04cc568040e3082db263a57086ea74a7d38c39e42/aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61", size = 1624061, upload-time = "2025-10-28T20:58:32.529Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c2/3b6034de81fbcc43de8aeb209073a2286dfb50b86e927b4efd81cf848197/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661", size = 1789201, upload-time = "2025-10-28T20:58:34.618Z" }, - { url = "https://files.pythonhosted.org/packages/c9/38/c15dcf6d4d890217dae79d7213988f4e5fe6183d43893a9cf2fe9e84ca8d/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98", size = 1776868, upload-time = "2025-10-28T20:58:38.835Z" }, - { url = "https://files.pythonhosted.org/packages/04/75/f74fd178ac81adf4f283a74847807ade5150e48feda6aef024403716c30c/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693", size = 1790660, upload-time = "2025-10-28T20:58:41.507Z" }, - { url = "https://files.pythonhosted.org/packages/e7/80/7368bd0d06b16b3aba358c16b919e9c46cf11587dc572091031b0e9e3ef0/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a", size = 1617548, upload-time = "2025-10-28T20:58:43.674Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4b/a6212790c50483cb3212e507378fbe26b5086d73941e1ec4b56a30439688/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be", size = 1817240, upload-time = "2025-10-28T20:58:45.787Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f7/ba5f0ba4ea8d8f3c32850912944532b933acbf0f3a75546b89269b9b7dde/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c", size = 1762334, upload-time = "2025-10-28T20:58:47.936Z" }, - { url = "https://files.pythonhosted.org/packages/7e/83/1a5a1856574588b1cad63609ea9ad75b32a8353ac995d830bf5da9357364/aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734", size = 464685, upload-time = "2025-10-28T20:58:50.642Z" }, - { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" }, -] - -[[package]] -name = "aiohttp-retry" -version = "2.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/61/ebda4d8e3d8cfa1fd3db0fb428db2dd7461d5742cea35178277ad180b033/aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1", size = 13608, upload-time = "2024-11-06T10:44:54.574Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/99/84ba7273339d0f3dfa57901b846489d2e5c2cd731470167757f1935fffbd/aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54", size = 9981, upload-time = "2024-11-06T10:44:52.917Z" }, -] - -[[package]] -name = "aiosignal" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, -] - -[[package]] -name = "anthropic" -version = "0.75.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "docstring-parser" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/1f/08e95f4b7e2d35205ae5dcbb4ae97e7d477fc521c275c02609e2931ece2d/anthropic-0.75.0.tar.gz", hash = "sha256:e8607422f4ab616db2ea5baacc215dd5f028da99ce2f022e33c7c535b29f3dfb", size = 439565, upload-time = "2025-11-24T20:41:45.28Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/1c/1cd02b7ae64302a6e06724bf80a96401d5313708651d277b1458504a1730/anthropic-0.75.0-py3-none-any.whl", hash = "sha256:ea8317271b6c15d80225a9f3c670152746e88805a7a61e14d4a374577164965b", size = 388164, upload-time = "2025-11-24T20:41:43.587Z" }, -] - -[[package]] -name = "anyio" -version = "4.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" }, -] - -[[package]] -name = "attrs" -version = "25.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, -] - -[[package]] -name = "cached-property" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/76/4b/3d870836119dbe9a5e3c9a61af8cc1a8b69d75aea564572e385882d5aefb/cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641", size = 10574, upload-time = "2024-10-25T15:43:55.667Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/0e/7d8225aab3bc1a0f5811f8e1b557aa034ac04bdf641925b30d3caf586b28/cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb", size = 7428, upload-time = "2024-10-25T15:43:54.711Z" }, -] - -[[package]] -name = "cachetools" -version = "6.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" }, -] - -[[package]] -name = "cbor2" -version = "5.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/8e/8b4fdde28e42ffcd741a37f4ffa9fb59cd4fe01625b544dfcfd9ccb54f01/cbor2-5.8.0.tar.gz", hash = "sha256:b19c35fcae9688ac01ef75bad5db27300c2537eb4ee00ed07e05d8456a0d4931", size = 107825, upload-time = "2025-12-30T18:44:22.455Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/4b/623435ef9b98e86b6956a41863d39ff4fe4d67983948b5834f55499681dd/cbor2-5.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:18ac191640093e6c7fbcb174c006ffec4106c3d8ab788e70272c1c4d933cbe11", size = 69875, upload-time = "2025-12-30T18:43:35.888Z" }, - { url = "https://files.pythonhosted.org/packages/58/17/f664201080b2a7d0f57c16c8e9e5922013b92f202e294863ec7e75b7ff7f/cbor2-5.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fddee9103a17d7bed5753f0c7fc6663faa506eb953e50d8287804eccf7b048e6", size = 268316, upload-time = "2025-12-30T18:43:37.161Z" }, - { url = "https://files.pythonhosted.org/packages/d0/e1/072745b4ff01afe9df2cd627f8fc51a1acedb5d3d1253765625d2929db91/cbor2-5.8.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8d2ea26fad620aba5e88d7541be8b10c5034a55db9a23809b7cb49f36803f05b", size = 258874, upload-time = "2025-12-30T18:43:38.878Z" }, - { url = "https://files.pythonhosted.org/packages/a7/10/61c262b886d22b62c56e8aac6d10fa06d0953c997879ab882a31a624952b/cbor2-5.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:de68b4b310b072b082d317adc4c5e6910173a6d9455412e6183d72c778d1f54c", size = 261971, upload-time = "2025-12-30T18:43:40.401Z" }, - { url = "https://files.pythonhosted.org/packages/7e/42/b7862f5e64364b10ad120ea53e87ec7e891fb268cb99c572348e647cf7e9/cbor2-5.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:418d2cf0e03e90160fa1474c05a40fe228bbb4a92d1628bdbbd13a48527cb34d", size = 254151, upload-time = "2025-12-30T18:43:41.938Z" }, - { url = "https://files.pythonhosted.org/packages/16/6a/8d3636cf75466c18615e7cfac0d345ee3c030f6c79535faed0c2c02b1839/cbor2-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:453200ffa1c285ea46ab5745736a015526d41f22da09cb45594624581d959770", size = 69169, upload-time = "2025-12-30T18:43:43.424Z" }, - { url = "https://files.pythonhosted.org/packages/9b/88/79b205bf869558b39a11de70750cb13679b27ba5654a43bed3f2aee7d1b4/cbor2-5.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:f6615412fca973a8b472b3efc4dab01df71cc13f15d8b2c0a1cffac44500f12d", size = 64955, upload-time = "2025-12-30T18:43:44.7Z" }, - { url = "https://files.pythonhosted.org/packages/2f/4f/3a16e3e8fd7e5fd86751a4f1aad218a8d19a96e75ec3989c3e95a8fe1d8f/cbor2-5.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b3f91fa699a5ce22470e973601c62dd9d55dc3ca20ee446516ac075fcab27c9", size = 70270, upload-time = "2025-12-30T18:43:46.005Z" }, - { url = "https://files.pythonhosted.org/packages/38/81/0d0cf0796fe8081492a61c45278f03def21a929535a492dd97c8438f5dbe/cbor2-5.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:518c118a5e00001854adb51f3164e647aa99b6a9877d2a733a28cb5c0a4d6857", size = 286242, upload-time = "2025-12-30T18:43:47.026Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a9/fdab6c10190cfb8d639e01f2b168f2406fc847a2a6bc00e7de78c3381d0a/cbor2-5.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cff2a1999e49cd51c23d1b6786a012127fd8f722c5946e82bd7ab3eb307443f3", size = 285412, upload-time = "2025-12-30T18:43:48.563Z" }, - { url = "https://files.pythonhosted.org/packages/31/59/746a8e630996217a3afd523f583fcf7e3d16640d63f9a03f0f4e4f74b5b1/cbor2-5.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c4492160212374973cdc14e46f0565f2462721ef922b40f7ea11e7d613dfb2a", size = 278041, upload-time = "2025-12-30T18:43:49.92Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a3/f3bbeb6dedd45c6e0cddd627ea790dea295eaf82c83f0e2159b733365ebd/cbor2-5.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:546c7c7c4c6bcdc54a59242e0e82cea8f332b17b4465ae628718fef1fce401ca", size = 278185, upload-time = "2025-12-30T18:43:51.192Z" }, - { url = "https://files.pythonhosted.org/packages/67/e5/9013d6b857ceb6cdb2851ffb5a887f53f2bab934a528c9d6fa73d9989d84/cbor2-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:074f0fa7535dd7fdee247c2c99f679d94f3aa058ccb1ccf4126cc72d6d89cbae", size = 69817, upload-time = "2025-12-30T18:43:52.352Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ab/7aa94ba3d44ecbc3a97bdb2fb6a8298063fe2e0b611e539a6fe41e36da20/cbor2-5.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:f95fed480b2a0d843f294d2a1ef4cc0f6a83c7922927f9f558e1f5a8dc54b7ca", size = 64923, upload-time = "2025-12-30T18:43:53.719Z" }, - { url = "https://files.pythonhosted.org/packages/a6/0d/5a3f20bafaefeb2c1903d961416f051c0950f0d09e7297a3aa6941596b29/cbor2-5.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d8d104480845e2f28c6165b4c961bbe58d08cb5638f368375cfcae051c28015", size = 70332, upload-time = "2025-12-30T18:43:54.694Z" }, - { url = "https://files.pythonhosted.org/packages/57/66/177a3f089e69db69c987453ab4934086408c3338551e4984734597be9f80/cbor2-5.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43efee947e5ab67d406d6e0dc61b5dee9d2f5e89ae176f90677a3741a20ca2e7", size = 285985, upload-time = "2025-12-30T18:43:55.733Z" }, - { url = "https://files.pythonhosted.org/packages/b7/8e/9e17b8e4ed80a2ce97e2dfa5915c169dbb31599409ddb830f514b57f96cc/cbor2-5.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be7ae582f50be539e09c134966d0fd63723fc4789b8dff1f6c2e3f24ae3eaf32", size = 285173, upload-time = "2025-12-30T18:43:57.321Z" }, - { url = "https://files.pythonhosted.org/packages/cc/33/9f92e107d78f88ac22723ac15d0259d220ba98c1d855e51796317f4c4114/cbor2-5.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50f5c709561a71ea7970b4cd2bf9eda4eccacc0aac212577080fdfe64183e7f5", size = 278395, upload-time = "2025-12-30T18:43:58.497Z" }, - { url = "https://files.pythonhosted.org/packages/2f/3f/46b80050a4a35ce5cf7903693864a9fdea7213567dc8faa6e25cb375c182/cbor2-5.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6790ecc73aa93e76d2d9076fc42bf91a9e69f2295e5fa702e776dbe986465bd", size = 278330, upload-time = "2025-12-30T18:43:59.656Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d2/d41f8c04c783a4d204e364be2d38043d4f732a3bed6f4c732e321cf34c7b/cbor2-5.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:c114af8099fa65a19a514db87ce7a06e942d8fea2730afd49be39f8e16e7f5e0", size = 69841, upload-time = "2025-12-30T18:44:01.159Z" }, - { url = "https://files.pythonhosted.org/packages/1b/8c/0397a82f6e67665009951453c83058e4c77ba54b9a9017ede56d6870306c/cbor2-5.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:ab3ba00494ad8669a459b12a558448d309c271fa4f89b116ad496ee35db38fea", size = 64982, upload-time = "2025-12-30T18:44:02.138Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0c/0654233d7543ac8a50f4785f172430ddc97538ba418eb305d6e529d1a120/cbor2-5.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ad72381477133046ce217617d839ea4e9454f8b77d9a6351b229e214102daeb7", size = 70710, upload-time = "2025-12-30T18:44:03.209Z" }, - { url = "https://files.pythonhosted.org/packages/84/62/4671d24e557d7f5a74a01b422c538925140c0495e57decde7e566f91d029/cbor2-5.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6da25190fad3434ce99876b11d4ca6b8828df6ca232cf7344cd14ae1166fb718", size = 285005, upload-time = "2025-12-30T18:44:05.109Z" }, - { url = "https://files.pythonhosted.org/packages/87/85/0c67d763a08e848c9a80d7e4723ba497cce676f41bc7ca1828ae90a0a872/cbor2-5.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c13919e3a24c5a6d286551fa288848a4cedc3e507c58a722ccd134e461217d99", size = 282435, upload-time = "2025-12-30T18:44:06.465Z" }, - { url = "https://files.pythonhosted.org/packages/b2/01/0650972b4dbfbebcfbe37cbba7fc3cd9019a8da6397ab3446e07175e342b/cbor2-5.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f8c40d32e5972047a777f9bf730870828f3cf1c43b3eb96fd0429c57a1d3b9e6", size = 277493, upload-time = "2025-12-30T18:44:07.609Z" }, - { url = "https://files.pythonhosted.org/packages/b3/6c/7704a4f32adc7f10f3b41ec067f500a4458f7606397af5e4cf2d368fd288/cbor2-5.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7627894bc0b3d5d0807f31e3107e11b996205470c4429dc2bb4ef8bfe7f64e1e", size = 276085, upload-time = "2025-12-30T18:44:09.021Z" }, - { url = "https://files.pythonhosted.org/packages/88/6d/e43452347630efe8133f5304127539100d937c138c0996d27ec63963ec2c/cbor2-5.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:b51c5e59becae746ca4de2bbaa8a2f5c64a68fec05cea62941b1a84a8335f7d1", size = 71657, upload-time = "2025-12-30T18:44:10.162Z" }, - { url = "https://files.pythonhosted.org/packages/8b/66/9a780ef34ab10a0437666232e885378cdd5f60197b1b5e61a62499e5a10a/cbor2-5.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:53b630f4db4b9f477ad84077283dd17ecf9894738aa17ef4938c369958e02a71", size = 67171, upload-time = "2025-12-30T18:44:11.619Z" }, - { url = "https://files.pythonhosted.org/packages/d6/4f/101071f880b4da05771128c0b89f41e334cff044dee05fb013c8f4be661c/cbor2-5.8.0-py3-none-any.whl", hash = "sha256:3727d80f539567b03a7aa11890e57798c67092c38df9e6c23abb059e0f65069c", size = 24374, upload-time = "2025-12-30T18:44:21.476Z" }, -] - -[[package]] -name = "certifi" -version = "2025.11.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, -] - -[[package]] -name = "cfgv" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, - { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, - { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, - { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, - { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, - { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, - { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, - { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, - { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, - { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, - { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, - { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, - { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, - { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, - { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, - { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, - { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, - { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, - { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, - { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, - { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, - { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, - { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, - { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, - { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, - { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, - { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, - { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, - { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, - { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, - { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, - { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, - { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, - { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, - { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, - { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, - { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, - { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, - { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, - { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, - { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, - { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, - { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, - { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, - { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, - { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, - { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, - { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, - { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, - { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, - { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, - { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, - { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - -[[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, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "coverage" -version = "7.13.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/f9/e92df5e07f3fc8d4c7f9a0f146ef75446bf870351cd37b788cf5897f8079/coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd", size = 825862, upload-time = "2025-12-28T15:42:56.969Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88", size = 218755, upload-time = "2025-12-28T15:40:30.812Z" }, - { url = "https://files.pythonhosted.org/packages/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3", size = 219257, upload-time = "2025-12-28T15:40:32.333Z" }, - { url = "https://files.pythonhosted.org/packages/01/d5/b11ef7863ffbbdb509da0023fad1e9eda1c0eaea61a6d2ea5b17d4ac706e/coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9", size = 249657, upload-time = "2025-12-28T15:40:34.1Z" }, - { url = "https://files.pythonhosted.org/packages/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee", size = 251581, upload-time = "2025-12-28T15:40:36.131Z" }, - { url = "https://files.pythonhosted.org/packages/82/f6/ebcfed11036ade4c0d75fa4453a6282bdd225bc073862766eec184a4c643/coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf", size = 253691, upload-time = "2025-12-28T15:40:37.626Z" }, - { url = "https://files.pythonhosted.org/packages/02/92/af8f5582787f5d1a8b130b2dcba785fa5e9a7a8e121a0bb2220a6fdbdb8a/coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3", size = 249799, upload-time = "2025-12-28T15:40:39.47Z" }, - { url = "https://files.pythonhosted.org/packages/24/aa/0e39a2a3b16eebf7f193863323edbff38b6daba711abaaf807d4290cf61a/coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef", size = 251389, upload-time = "2025-12-28T15:40:40.954Z" }, - { url = "https://files.pythonhosted.org/packages/73/46/7f0c13111154dc5b978900c0ccee2e2ca239b910890e674a77f1363d483e/coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851", size = 249450, upload-time = "2025-12-28T15:40:42.489Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ca/e80da6769e8b669ec3695598c58eef7ad98b0e26e66333996aee6316db23/coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb", size = 249170, upload-time = "2025-12-28T15:40:44.279Z" }, - { url = "https://files.pythonhosted.org/packages/af/18/9e29baabdec1a8644157f572541079b4658199cfd372a578f84228e860de/coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba", size = 250081, upload-time = "2025-12-28T15:40:45.748Z" }, - { url = "https://files.pythonhosted.org/packages/00/f8/c3021625a71c3b2f516464d322e41636aea381018319050a8114105872ee/coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19", size = 221281, upload-time = "2025-12-28T15:40:47.232Z" }, - { url = "https://files.pythonhosted.org/packages/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a", size = 222215, upload-time = "2025-12-28T15:40:49.19Z" }, - { url = "https://files.pythonhosted.org/packages/5c/9a/be342e76f6e531cae6406dc46af0d350586f24d9b67fdfa6daee02df71af/coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c", size = 220886, upload-time = "2025-12-28T15:40:51.067Z" }, - { url = "https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3", size = 218927, upload-time = "2025-12-28T15:40:52.814Z" }, - { url = "https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e", size = 219288, upload-time = "2025-12-28T15:40:54.262Z" }, - { url = "https://files.pythonhosted.org/packages/d0/0a/853a76e03b0f7c4375e2ca025df45c918beb367f3e20a0a8e91967f6e96c/coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c", size = 250786, upload-time = "2025-12-28T15:40:56.059Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62", size = 253543, upload-time = "2025-12-28T15:40:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/96/b2/7f1f0437a5c855f87e17cf5d0dc35920b6440ff2b58b1ba9788c059c26c8/coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968", size = 254635, upload-time = "2025-12-28T15:40:59.443Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d1/73c3fdb8d7d3bddd9473c9c6a2e0682f09fc3dfbcb9c3f36412a7368bcab/coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e", size = 251202, upload-time = "2025-12-28T15:41:01.328Z" }, - { url = "https://files.pythonhosted.org/packages/66/3c/f0edf75dcc152f145d5598329e864bbbe04ab78660fe3e8e395f9fff010f/coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f", size = 252566, upload-time = "2025-12-28T15:41:03.319Z" }, - { url = "https://files.pythonhosted.org/packages/17/b3/e64206d3c5f7dcbceafd14941345a754d3dbc78a823a6ed526e23b9cdaab/coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee", size = 250711, upload-time = "2025-12-28T15:41:06.411Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ad/28a3eb970a8ef5b479ee7f0c484a19c34e277479a5b70269dc652b730733/coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf", size = 250278, upload-time = "2025-12-28T15:41:08.285Z" }, - { url = "https://files.pythonhosted.org/packages/54/e3/c8f0f1a93133e3e1291ca76cbb63565bd4b5c5df63b141f539d747fff348/coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c", size = 252154, upload-time = "2025-12-28T15:41:09.969Z" }, - { url = "https://files.pythonhosted.org/packages/d0/bf/9939c5d6859c380e405b19e736321f1c7d402728792f4c752ad1adcce005/coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7", size = 221487, upload-time = "2025-12-28T15:41:11.468Z" }, - { url = "https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6", size = 222299, upload-time = "2025-12-28T15:41:13.386Z" }, - { url = "https://files.pythonhosted.org/packages/10/79/176a11203412c350b3e9578620013af35bcdb79b651eb976f4a4b32044fa/coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c", size = 220941, upload-time = "2025-12-28T15:41:14.975Z" }, - { url = "https://files.pythonhosted.org/packages/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78", size = 218951, upload-time = "2025-12-28T15:41:16.653Z" }, - { url = "https://files.pythonhosted.org/packages/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b", size = 219325, upload-time = "2025-12-28T15:41:18.609Z" }, - { url = "https://files.pythonhosted.org/packages/59/f6/efdabdb4929487baeb7cb2a9f7dac457d9356f6ad1b255be283d58b16316/coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd", size = 250309, upload-time = "2025-12-28T15:41:20.629Z" }, - { url = "https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992", size = 252907, upload-time = "2025-12-28T15:41:22.257Z" }, - { url = "https://files.pythonhosted.org/packages/75/38/f1ea837e3dc1231e086db1638947e00d264e7e8c41aa8ecacf6e1e0c05f4/coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4", size = 254148, upload-time = "2025-12-28T15:41:23.87Z" }, - { url = "https://files.pythonhosted.org/packages/7f/43/f4f16b881aaa34954ba446318dea6b9ed5405dd725dd8daac2358eda869a/coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a", size = 250515, upload-time = "2025-12-28T15:41:25.437Z" }, - { url = "https://files.pythonhosted.org/packages/84/34/8cba7f00078bd468ea914134e0144263194ce849ec3baad187ffb6203d1c/coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766", size = 252292, upload-time = "2025-12-28T15:41:28.459Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/cffac66c7652d84ee4ac52d3ccb94c015687d3b513f9db04bfcac2ac800d/coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4", size = 250242, upload-time = "2025-12-28T15:41:30.02Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/9a64d462263dde416f3c0067efade7b52b52796f489b1037a95b0dc389c9/coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398", size = 250068, upload-time = "2025-12-28T15:41:32.007Z" }, - { url = "https://files.pythonhosted.org/packages/69/c8/a8994f5fece06db7c4a97c8fc1973684e178599b42e66280dded0524ef00/coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784", size = 251846, upload-time = "2025-12-28T15:41:33.946Z" }, - { url = "https://files.pythonhosted.org/packages/cc/f7/91fa73c4b80305c86598a2d4e54ba22df6bf7d0d97500944af7ef155d9f7/coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461", size = 221512, upload-time = "2025-12-28T15:41:35.519Z" }, - { url = "https://files.pythonhosted.org/packages/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500", size = 222321, upload-time = "2025-12-28T15:41:37.371Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b8/bdcb7253b7e85157282450262008f1366aa04663f3e3e4c30436f596c3e2/coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9", size = 220949, upload-time = "2025-12-28T15:41:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/70/52/f2be52cc445ff75ea8397948c96c1b4ee14f7f9086ea62fc929c5ae7b717/coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc", size = 219643, upload-time = "2025-12-28T15:41:41.567Z" }, - { url = "https://files.pythonhosted.org/packages/47/79/c85e378eaa239e2edec0c5523f71542c7793fe3340954eafb0bc3904d32d/coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a", size = 219997, upload-time = "2025-12-28T15:41:43.418Z" }, - { url = "https://files.pythonhosted.org/packages/fe/9b/b1ade8bfb653c0bbce2d6d6e90cc6c254cbb99b7248531cc76253cb4da6d/coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4", size = 261296, upload-time = "2025-12-28T15:41:45.207Z" }, - { url = "https://files.pythonhosted.org/packages/1f/af/ebf91e3e1a2473d523e87e87fd8581e0aa08741b96265730e2d79ce78d8d/coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6", size = 263363, upload-time = "2025-12-28T15:41:47.163Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8b/fb2423526d446596624ac7fde12ea4262e66f86f5120114c3cfd0bb2befa/coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1", size = 265783, upload-time = "2025-12-28T15:41:49.03Z" }, - { url = "https://files.pythonhosted.org/packages/9b/26/ef2adb1e22674913b89f0fe7490ecadcef4a71fa96f5ced90c60ec358789/coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd", size = 260508, upload-time = "2025-12-28T15:41:51.035Z" }, - { url = "https://files.pythonhosted.org/packages/ce/7d/f0f59b3404caf662e7b5346247883887687c074ce67ba453ea08c612b1d5/coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c", size = 263357, upload-time = "2025-12-28T15:41:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/1a/b1/29896492b0b1a047604d35d6fa804f12818fa30cdad660763a5f3159e158/coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0", size = 260978, upload-time = "2025-12-28T15:41:54.589Z" }, - { url = "https://files.pythonhosted.org/packages/48/f2/971de1238a62e6f0a4128d37adadc8bb882ee96afbe03ff1570291754629/coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e", size = 259877, upload-time = "2025-12-28T15:41:56.263Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fc/0474efcbb590ff8628830e9aaec5f1831594874360e3251f1fdec31d07a3/coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53", size = 262069, upload-time = "2025-12-28T15:41:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/88/4f/3c159b7953db37a7b44c0eab8a95c37d1aa4257c47b4602c04022d5cb975/coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842", size = 222184, upload-time = "2025-12-28T15:41:59.763Z" }, - { url = "https://files.pythonhosted.org/packages/58/a5/6b57d28f81417f9335774f20679d9d13b9a8fb90cd6160957aa3b54a2379/coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2", size = 223250, upload-time = "2025-12-28T15:42:01.52Z" }, - { url = "https://files.pythonhosted.org/packages/81/7c/160796f3b035acfbb58be80e02e484548595aa67e16a6345e7910ace0a38/coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09", size = 221521, upload-time = "2025-12-28T15:42:03.275Z" }, - { url = "https://files.pythonhosted.org/packages/aa/8e/ba0e597560c6563fc0adb902fda6526df5d4aa73bb10adf0574d03bd2206/coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894", size = 218996, upload-time = "2025-12-28T15:42:04.978Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8e/764c6e116f4221dc7aa26c4061181ff92edb9c799adae6433d18eeba7a14/coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a", size = 219326, upload-time = "2025-12-28T15:42:06.691Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a6/6130dc6d8da28cdcbb0f2bf8865aeca9b157622f7c0031e48c6cf9a0e591/coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f", size = 250374, upload-time = "2025-12-28T15:42:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/82/2b/783ded568f7cd6b677762f780ad338bf4b4750205860c17c25f7c708995e/coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909", size = 252882, upload-time = "2025-12-28T15:42:10.515Z" }, - { url = "https://files.pythonhosted.org/packages/cd/b2/9808766d082e6a4d59eb0cc881a57fc1600eb2c5882813eefff8254f71b5/coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4", size = 254218, upload-time = "2025-12-28T15:42:12.208Z" }, - { url = "https://files.pythonhosted.org/packages/44/ea/52a985bb447c871cb4d2e376e401116520991b597c85afdde1ea9ef54f2c/coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75", size = 250391, upload-time = "2025-12-28T15:42:14.21Z" }, - { url = "https://files.pythonhosted.org/packages/7f/1d/125b36cc12310718873cfc8209ecfbc1008f14f4f5fa0662aa608e579353/coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9", size = 252239, upload-time = "2025-12-28T15:42:16.292Z" }, - { url = "https://files.pythonhosted.org/packages/6a/16/10c1c164950cade470107f9f14bbac8485f8fb8515f515fca53d337e4a7f/coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465", size = 250196, upload-time = "2025-12-28T15:42:18.54Z" }, - { url = "https://files.pythonhosted.org/packages/2a/c6/cd860fac08780c6fd659732f6ced1b40b79c35977c1356344e44d72ba6c4/coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864", size = 250008, upload-time = "2025-12-28T15:42:20.365Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/a8c58d3d38f82a5711e1e0a67268362af48e1a03df27c03072ac30feefcf/coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9", size = 251671, upload-time = "2025-12-28T15:42:22.114Z" }, - { url = "https://files.pythonhosted.org/packages/f0/bc/fd4c1da651d037a1e3d53e8cb3f8182f4b53271ffa9a95a2e211bacc0349/coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5", size = 221777, upload-time = "2025-12-28T15:42:23.919Z" }, - { url = "https://files.pythonhosted.org/packages/4b/50/71acabdc8948464c17e90b5ffd92358579bd0910732c2a1c9537d7536aa6/coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a", size = 222592, upload-time = "2025-12-28T15:42:25.619Z" }, - { url = "https://files.pythonhosted.org/packages/f7/c8/a6fb943081bb0cc926499c7907731a6dc9efc2cbdc76d738c0ab752f1a32/coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0", size = 221169, upload-time = "2025-12-28T15:42:27.629Z" }, - { url = "https://files.pythonhosted.org/packages/16/61/d5b7a0a0e0e40d62e59bc8c7aa1afbd86280d82728ba97f0673b746b78e2/coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a", size = 219730, upload-time = "2025-12-28T15:42:29.306Z" }, - { url = "https://files.pythonhosted.org/packages/a3/2c/8881326445fd071bb49514d1ce97d18a46a980712b51fee84f9ab42845b4/coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6", size = 220001, upload-time = "2025-12-28T15:42:31.319Z" }, - { url = "https://files.pythonhosted.org/packages/b5/d7/50de63af51dfa3a7f91cc37ad8fcc1e244b734232fbc8b9ab0f3c834a5cd/coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673", size = 261370, upload-time = "2025-12-28T15:42:32.992Z" }, - { url = "https://files.pythonhosted.org/packages/e1/2c/d31722f0ec918fd7453b2758312729f645978d212b410cd0f7c2aed88a94/coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5", size = 263485, upload-time = "2025-12-28T15:42:34.759Z" }, - { url = "https://files.pythonhosted.org/packages/fa/7a/2c114fa5c5fc08ba0777e4aec4c97e0b4a1afcb69c75f1f54cff78b073ab/coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d", size = 265890, upload-time = "2025-12-28T15:42:36.517Z" }, - { url = "https://files.pythonhosted.org/packages/65/d9/f0794aa1c74ceabc780fe17f6c338456bbc4e96bd950f2e969f48ac6fb20/coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8", size = 260445, upload-time = "2025-12-28T15:42:38.646Z" }, - { url = "https://files.pythonhosted.org/packages/49/23/184b22a00d9bb97488863ced9454068c79e413cb23f472da6cbddc6cfc52/coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486", size = 263357, upload-time = "2025-12-28T15:42:40.788Z" }, - { url = "https://files.pythonhosted.org/packages/7d/bd/58af54c0c9199ea4190284f389005779d7daf7bf3ce40dcd2d2b2f96da69/coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564", size = 260959, upload-time = "2025-12-28T15:42:42.808Z" }, - { url = "https://files.pythonhosted.org/packages/4b/2a/6839294e8f78a4891bf1df79d69c536880ba2f970d0ff09e7513d6e352e9/coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7", size = 259792, upload-time = "2025-12-28T15:42:44.818Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c3/528674d4623283310ad676c5af7414b9850ab6d55c2300e8aa4b945ec554/coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416", size = 262123, upload-time = "2025-12-28T15:42:47.108Z" }, - { url = "https://files.pythonhosted.org/packages/06/c5/8c0515692fb4c73ac379d8dc09b18eaf0214ecb76ea6e62467ba7a1556ff/coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f", size = 222562, upload-time = "2025-12-28T15:42:49.144Z" }, - { url = "https://files.pythonhosted.org/packages/05/0e/c0a0c4678cb30dac735811db529b321d7e1c9120b79bd728d4f4d6b010e9/coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79", size = 223670, upload-time = "2025-12-28T15:42:51.218Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/b177aa0011f354abf03a8f30a85032686d290fdeed4222b27d36b4372a50/coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4", size = 221707, upload-time = "2025-12-28T15:42:53.034Z" }, - { url = "https://files.pythonhosted.org/packages/cc/48/d9f421cb8da5afaa1a64570d9989e00fb7955e6acddc5a12979f7666ef60/coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573", size = 210722, upload-time = "2025-12-28T15:42:54.901Z" }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "daytona" -version = "0.134.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "daytona-api-client" }, - { name = "daytona-api-client-async" }, - { name = "daytona-toolbox-api-client" }, - { name = "daytona-toolbox-api-client-async" }, - { name = "deprecated" }, - { name = "environs" }, - { name = "httpx" }, - { name = "multipart" }, - { name = "obstore" }, - { name = "pydantic" }, - { name = "toml" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/b7/6e7374b10497eb13484315981f1b0c558543f9fab15c8fe05c9a3532c0f0/daytona-0.134.0.tar.gz", hash = "sha256:15182de76037f0602ddddb4f21010fe8b6b50cd93047fb0ce192261aea433bd8", size = 114378, upload-time = "2026-01-21T13:50:56.076Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/3c/4ee7af9f5d6803bad9f24fc14d550cd61b6cefd13fc337c86e2d23550ad6/daytona-0.134.0-py3-none-any.whl", hash = "sha256:d44be4aaa276832a955954048715e13fda315505fb3ffbb7e4fc1a5b47a590d6", size = 142617, upload-time = "2026-01-21T13:50:53.65Z" }, -] - -[[package]] -name = "daytona-api-client" -version = "0.134.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/f3/252be41b2d1176065aed2aa7b5443364c9ae259ba9678f0efc2f567f5b88/daytona_api_client-0.134.0.tar.gz", hash = "sha256:8438b37c3a2b22a5779a96909711de3fb94181e8837e34c771eb7ff37d4004a1", size = 134325, upload-time = "2026-01-21T13:23:19.333Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/20/d3d3f6676333bcee02848d59a06a7c4ed862ce78778a1cec30c982a26eed/daytona_api_client-0.134.0-py3-none-any.whl", hash = "sha256:d7912b2c73965cc27c05e9da92697db79d5b793949f3087af483dfcafd859902", size = 373697, upload-time = "2026-01-21T13:23:18.153Z" }, -] - -[[package]] -name = "daytona-api-client-async" -version = "0.134.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aiohttp-retry" }, - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/5a/9264ab5c666d81f9a38f0dcd34fca40ef2052da37d2debf461db00614866/daytona_api_client_async-0.134.0.tar.gz", hash = "sha256:36f750442944974cf4ae6e02e81decfb68e18033b3350af74cd9a31aeb41e0d0", size = 134418, upload-time = "2026-01-21T13:23:31.415Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/6e/1077729588a32b154d07e1d656d4ea8f99b8a111fce4674a22cc651bce00/daytona_api_client_async-0.134.0-py3-none-any.whl", hash = "sha256:d357ae15bab1042f2629133b9eb050b8f1ed77948fb1fc41342fdcb64564efa7", size = 376545, upload-time = "2026-01-21T13:23:30.282Z" }, -] - -[[package]] -name = "daytona-toolbox-api-client" -version = "0.134.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3b/94/26763cf220f9af9377d04bee08bfb2655e08b634a758c37bf4f04ad4fc6f/daytona_toolbox_api_client-0.134.0.tar.gz", hash = "sha256:0c2c0f571f006c8e33e9ee9b293c191d95854e2531a2628252a339f2a28d829e", size = 61700, upload-time = "2026-01-21T13:23:48.311Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/d3/fccbf2498309aa339cc086d797397dd12716d70283dcf560a8d240a5589f/daytona_toolbox_api_client-0.134.0-py3-none-any.whl", hash = "sha256:117f35c52fb28be0194a73c991074d997413927e94c630452f1669fdff1a29af", size = 162010, upload-time = "2026-01-21T13:23:47.395Z" }, -] - -[[package]] -name = "daytona-toolbox-api-client-async" -version = "0.134.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "aiohttp-retry" }, - { name = "pydantic" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/a3/75cda8c35720ecd3356ac7974e9e6146381d12b833e5ab7c320a238376f2/daytona_toolbox_api_client_async-0.134.0.tar.gz", hash = "sha256:a7622b9762dbf61c10837170404ab9be69a1149d7d5f51a6f3b5602890514013", size = 58647, upload-time = "2026-01-21T13:23:44.744Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/98/9c8d359a6f4258d300e20daae130578716ff88e2531ba376522d72a8874e/daytona_toolbox_api_client_async-0.134.0-py3-none-any.whl", hash = "sha256:18f3f3dbfa847c35ffb85f297ac08e78c5ddcbad3a84cb7f7a96133ad76130ce", size = 163255, upload-time = "2026-01-21T13:23:42.905Z" }, -] - -[[package]] -name = "deprecated" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, -] - -[[package]] -name = "dill" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, -] - -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, -] - -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, -] - -[[package]] -name = "docstring-parser" -version = "0.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, -] - -[[package]] -name = "environs" -version = "14.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "marshmallow" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/aa/75/06801d5beeb398ed3903167af9376bb81c4ac41c44a53d45193065ebb1a8/environs-14.5.0.tar.gz", hash = "sha256:f7b8f6fcf3301bc674bc9c03e39b5986d116126ffb96764efd34c339ed9464ee", size = 35426, upload-time = "2025-11-02T21:30:36.78Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/f3/6961beb9a1e77d01dee1dd48f00fb3064429c8abcfa26aa863eb7cb2b6dd/environs-14.5.0-py3-none-any.whl", hash = "sha256:1abd3e3a5721fb09797438d6c902bc2f35d4580dfaffe68b8ee588b67b504e13", size = 17202, upload-time = "2025-11-02T21:30:35.186Z" }, -] - -[[package]] -name = "filelock" -version = "3.20.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" }, -] - -[[package]] -name = "frozenlist" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, - { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, - { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, - { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, - { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, - { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, - { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, - { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, - { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, - { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, - { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, - { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, - { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, - { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, - { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, - { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, - { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, - { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, - { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, - { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, - { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, - { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, - { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, - { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, - { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, - { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, - { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, - { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, - { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, - { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, - { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, - { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, - { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, - { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, - { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, - { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, - { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, - { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, - { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, - { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, - { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, - { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, - { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, - { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, - { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, - { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, - { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, - { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, - { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, - { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, - { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, - { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, - { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, - { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, - { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, - { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, - { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, - { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, - { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, - { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, - { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, - { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, - { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, - { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, - { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, - { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, - { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, - { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, - { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, - { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, - { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, - { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, -] - -[[package]] -name = "google-auth" -version = "2.45.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "pyasn1-modules" }, - { name = "rsa" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e5/00/3c794502a8b892c404b2dea5b3650eb21bfc7069612fbfd15c7f17c1cb0d/google_auth-2.45.0.tar.gz", hash = "sha256:90d3f41b6b72ea72dd9811e765699ee491ab24139f34ebf1ca2b9cc0c38708f3", size = 320708, upload-time = "2025-12-15T22:58:42.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/97/451d55e05487a5cd6279a01a7e34921858b16f7dc8aa38a2c684743cd2b3/google_auth-2.45.0-py2.py3-none-any.whl", hash = "sha256:82344e86dc00410ef5382d99be677c6043d72e502b625aa4f4afa0bdacca0f36", size = 233312, upload-time = "2025-12-15T22:58:40.777Z" }, -] - -[package.optional-dependencies] -requests = [ - { name = "requests" }, -] - -[[package]] -name = "google-genai" -version = "1.56.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "google-auth", extra = ["requests"] }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "sniffio" }, - { name = "tenacity" }, - { name = "typing-extensions" }, - { name = "websockets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/70/ad/d3ac5a102135bd3f1e4b1475ca65d2bd4bcc22eb2e9348ac40fe3fadb1d6/google_genai-1.56.0.tar.gz", hash = "sha256:0491af33c375f099777ae207d9621f044e27091fafad4c50e617eba32165e82f", size = 340451, upload-time = "2025-12-17T12:35:05.412Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/93/94bc7a89ef4e7ed3666add55cd859d1483a22737251df659bf1aa46e9405/google_genai-1.56.0-py3-none-any.whl", hash = "sha256:9e6b11e0c105ead229368cb5849a480e4d0185519f8d9f538d61ecfcf193b052", size = 426563, upload-time = "2025-12-17T12:35:03.717Z" }, -] - -[[package]] -name = "grpclib" -version = "0.4.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "h2" }, - { name = "multidict" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/28/5a2c299ec82a876a252c5919aa895a6f1d1d35c96417c5ce4a4660dc3a80/grpclib-0.4.9.tar.gz", hash = "sha256:cc589c330fa81004c6400a52a566407574498cb5b055fa927013361e21466c46", size = 84798, upload-time = "2025-12-14T22:23:14.349Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/90/b0cbbd9efcc82816c58f31a34963071aa19fb792a212a5d9caf8e0fc3097/grpclib-0.4.9-py3-none-any.whl", hash = "sha256:7762ec1c8ed94dfad597475152dd35cbd11aecaaca2f243e29702435ca24cf0e", size = 77063, upload-time = "2025-12-14T22:23:13.224Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, -] - -[[package]] -name = "h2" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hpack" }, - { name = "hyperframe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, -] - -[[package]] -name = "hpack" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[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, upload-time = "2024-12-06T15:37:23.222Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, -] - -[[package]] -name = "hyperframe" -version = "6.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, -] - -[[package]] -name = "identify" -version = "2.6.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "jiter" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/f9/eaca4633486b527ebe7e681c431f529b63fe2709e7c5242fc0f43f77ce63/jiter-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8f8a7e317190b2c2d60eb2e8aa835270b008139562d70fe732e1c0020ec53c9", size = 316435, upload-time = "2025-11-09T20:47:02.087Z" }, - { url = "https://files.pythonhosted.org/packages/10/c1/40c9f7c22f5e6ff715f28113ebaba27ab85f9af2660ad6e1dd6425d14c19/jiter-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2218228a077e784c6c8f1a8e5d6b8cb1dea62ce25811c356364848554b2056cd", size = 320548, upload-time = "2025-11-09T20:47:03.409Z" }, - { url = "https://files.pythonhosted.org/packages/6b/1b/efbb68fe87e7711b00d2cfd1f26bb4bfc25a10539aefeaa7727329ffb9cb/jiter-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9354ccaa2982bf2188fd5f57f79f800ef622ec67beb8329903abf6b10da7d423", size = 351915, upload-time = "2025-11-09T20:47:05.171Z" }, - { url = "https://files.pythonhosted.org/packages/15/2d/c06e659888c128ad1e838123d0638f0efad90cc30860cb5f74dd3f2fc0b3/jiter-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2607185ea89b4af9a604d4c7ec40e45d3ad03ee66998b031134bc510232bb7", size = 368966, upload-time = "2025-11-09T20:47:06.508Z" }, - { url = "https://files.pythonhosted.org/packages/6b/20/058db4ae5fb07cf6a4ab2e9b9294416f606d8e467fb74c2184b2a1eeacba/jiter-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a585a5e42d25f2e71db5f10b171f5e5ea641d3aa44f7df745aa965606111cc2", size = 482047, upload-time = "2025-11-09T20:47:08.382Z" }, - { url = "https://files.pythonhosted.org/packages/49/bb/dc2b1c122275e1de2eb12905015d61e8316b2f888bdaac34221c301495d6/jiter-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd9e21d34edff5a663c631f850edcb786719c960ce887a5661e9c828a53a95d9", size = 380835, upload-time = "2025-11-09T20:47:09.81Z" }, - { url = "https://files.pythonhosted.org/packages/23/7d/38f9cd337575349de16da575ee57ddb2d5a64d425c9367f5ef9e4612e32e/jiter-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a612534770470686cd5431478dc5a1b660eceb410abade6b1b74e320ca98de6", size = 364587, upload-time = "2025-11-09T20:47:11.529Z" }, - { url = "https://files.pythonhosted.org/packages/f0/a3/b13e8e61e70f0bb06085099c4e2462647f53cc2ca97614f7fedcaa2bb9f3/jiter-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3985aea37d40a908f887b34d05111e0aae822943796ebf8338877fee2ab67725", size = 390492, upload-time = "2025-11-09T20:47:12.993Z" }, - { url = "https://files.pythonhosted.org/packages/07/71/e0d11422ed027e21422f7bc1883c61deba2d9752b720538430c1deadfbca/jiter-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b1207af186495f48f72529f8d86671903c8c10127cac6381b11dddc4aaa52df6", size = 522046, upload-time = "2025-11-09T20:47:14.6Z" }, - { url = "https://files.pythonhosted.org/packages/9f/59/b968a9aa7102a8375dbbdfbd2aeebe563c7e5dddf0f47c9ef1588a97e224/jiter-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef2fb241de583934c9915a33120ecc06d94aa3381a134570f59eed784e87001e", size = 513392, upload-time = "2025-11-09T20:47:16.011Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e4/7df62002499080dbd61b505c5cb351aa09e9959d176cac2aa8da6f93b13b/jiter-0.12.0-cp311-cp311-win32.whl", hash = "sha256:453b6035672fecce8007465896a25b28a6b59cfe8fbc974b2563a92f5a92a67c", size = 206096, upload-time = "2025-11-09T20:47:17.344Z" }, - { url = "https://files.pythonhosted.org/packages/bb/60/1032b30ae0572196b0de0e87dce3b6c26a1eff71aad5fe43dee3082d32e0/jiter-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca264b9603973c2ad9435c71a8ec8b49f8f715ab5ba421c85a51cde9887e421f", size = 204899, upload-time = "2025-11-09T20:47:19.365Z" }, - { url = "https://files.pythonhosted.org/packages/49/d5/c145e526fccdb834063fb45c071df78b0cc426bbaf6de38b0781f45d956f/jiter-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:cb00ef392e7d684f2754598c02c409f376ddcef857aae796d559e6cacc2d78a5", size = 188070, upload-time = "2025-11-09T20:47:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449, upload-time = "2025-11-09T20:47:22.999Z" }, - { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855, upload-time = "2025-11-09T20:47:24.779Z" }, - { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171, upload-time = "2025-11-09T20:47:26.469Z" }, - { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590, upload-time = "2025-11-09T20:47:27.918Z" }, - { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462, upload-time = "2025-11-09T20:47:29.654Z" }, - { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983, upload-time = "2025-11-09T20:47:31.026Z" }, - { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328, upload-time = "2025-11-09T20:47:33.286Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740, upload-time = "2025-11-09T20:47:34.703Z" }, - { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875, upload-time = "2025-11-09T20:47:36.058Z" }, - { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457, upload-time = "2025-11-09T20:47:37.932Z" }, - { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546, upload-time = "2025-11-09T20:47:40.47Z" }, - { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196, upload-time = "2025-11-09T20:47:41.794Z" }, - { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100, upload-time = "2025-11-09T20:47:43.007Z" }, - { url = "https://files.pythonhosted.org/packages/3d/a6/97209693b177716e22576ee1161674d1d58029eb178e01866a0422b69224/jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e", size = 313658, upload-time = "2025-11-09T20:47:44.424Z" }, - { url = "https://files.pythonhosted.org/packages/06/4d/125c5c1537c7d8ee73ad3d530a442d6c619714b95027143f1b61c0b4dfe0/jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1", size = 318605, upload-time = "2025-11-09T20:47:45.973Z" }, - { url = "https://files.pythonhosted.org/packages/99/bf/a840b89847885064c41a5f52de6e312e91fa84a520848ee56c97e4fa0205/jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf", size = 349803, upload-time = "2025-11-09T20:47:47.535Z" }, - { url = "https://files.pythonhosted.org/packages/8a/88/e63441c28e0db50e305ae23e19c1d8fae012d78ed55365da392c1f34b09c/jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44", size = 365120, upload-time = "2025-11-09T20:47:49.284Z" }, - { url = "https://files.pythonhosted.org/packages/0a/7c/49b02714af4343970eb8aca63396bc1c82fa01197dbb1e9b0d274b550d4e/jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45", size = 479918, upload-time = "2025-11-09T20:47:50.807Z" }, - { url = "https://files.pythonhosted.org/packages/69/ba/0a809817fdd5a1db80490b9150645f3aae16afad166960bcd562be194f3b/jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87", size = 379008, upload-time = "2025-11-09T20:47:52.211Z" }, - { url = "https://files.pythonhosted.org/packages/5f/c3/c9fc0232e736c8877d9e6d83d6eeb0ba4e90c6c073835cc2e8f73fdeef51/jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed", size = 361785, upload-time = "2025-11-09T20:47:53.512Z" }, - { url = "https://files.pythonhosted.org/packages/96/61/61f69b7e442e97ca6cd53086ddc1cf59fb830549bc72c0a293713a60c525/jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9", size = 386108, upload-time = "2025-11-09T20:47:54.893Z" }, - { url = "https://files.pythonhosted.org/packages/e9/2e/76bb3332f28550c8f1eba3bf6e5efe211efda0ddbbaf24976bc7078d42a5/jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626", size = 519937, upload-time = "2025-11-09T20:47:56.253Z" }, - { url = "https://files.pythonhosted.org/packages/84/d6/fa96efa87dc8bff2094fb947f51f66368fa56d8d4fc9e77b25d7fbb23375/jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c", size = 510853, upload-time = "2025-11-09T20:47:58.32Z" }, - { url = "https://files.pythonhosted.org/packages/8a/28/93f67fdb4d5904a708119a6ab58a8f1ec226ff10a94a282e0215402a8462/jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de", size = 204699, upload-time = "2025-11-09T20:47:59.686Z" }, - { url = "https://files.pythonhosted.org/packages/c4/1f/30b0eb087045a0abe2a5c9c0c0c8da110875a1d3be83afd4a9a4e548be3c/jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a", size = 204258, upload-time = "2025-11-09T20:48:01.01Z" }, - { url = "https://files.pythonhosted.org/packages/2c/f4/2b4daf99b96bce6fc47971890b14b2a36aef88d7beb9f057fafa032c6141/jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60", size = 185503, upload-time = "2025-11-09T20:48:02.35Z" }, - { url = "https://files.pythonhosted.org/packages/39/ca/67bb15a7061d6fe20b9b2a2fd783e296a1e0f93468252c093481a2f00efa/jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6", size = 317965, upload-time = "2025-11-09T20:48:03.783Z" }, - { url = "https://files.pythonhosted.org/packages/18/af/1788031cd22e29c3b14bc6ca80b16a39a0b10e611367ffd480c06a259831/jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4", size = 345831, upload-time = "2025-11-09T20:48:05.55Z" }, - { url = "https://files.pythonhosted.org/packages/05/17/710bf8472d1dff0d3caf4ced6031060091c1320f84ee7d5dcbed1f352417/jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb", size = 361272, upload-time = "2025-11-09T20:48:06.951Z" }, - { url = "https://files.pythonhosted.org/packages/fb/f1/1dcc4618b59761fef92d10bcbb0b038b5160be653b003651566a185f1a5c/jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7", size = 204604, upload-time = "2025-11-09T20:48:08.328Z" }, - { url = "https://files.pythonhosted.org/packages/d9/32/63cb1d9f1c5c6632a783c0052cde9ef7ba82688f7065e2f0d5f10a7e3edb/jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3", size = 185628, upload-time = "2025-11-09T20:48:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/a8/99/45c9f0dbe4a1416b2b9a8a6d1236459540f43d7fb8883cff769a8db0612d/jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525", size = 312478, upload-time = "2025-11-09T20:48:10.898Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a7/54ae75613ba9e0f55fcb0bc5d1f807823b5167cc944e9333ff322e9f07dd/jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49", size = 318706, upload-time = "2025-11-09T20:48:12.266Z" }, - { url = "https://files.pythonhosted.org/packages/59/31/2aa241ad2c10774baf6c37f8b8e1f39c07db358f1329f4eb40eba179c2a2/jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1", size = 351894, upload-time = "2025-11-09T20:48:13.673Z" }, - { url = "https://files.pythonhosted.org/packages/54/4f/0f2759522719133a9042781b18cc94e335b6d290f5e2d3e6899d6af933e3/jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e", size = 365714, upload-time = "2025-11-09T20:48:15.083Z" }, - { url = "https://files.pythonhosted.org/packages/dc/6f/806b895f476582c62a2f52c453151edd8a0fde5411b0497baaa41018e878/jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e", size = 478989, upload-time = "2025-11-09T20:48:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/86/6c/012d894dc6e1033acd8db2b8346add33e413ec1c7c002598915278a37f79/jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff", size = 378615, upload-time = "2025-11-09T20:48:18.614Z" }, - { url = "https://files.pythonhosted.org/packages/87/30/d718d599f6700163e28e2c71c0bbaf6dace692e7df2592fd793ac9276717/jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a", size = 364745, upload-time = "2025-11-09T20:48:20.117Z" }, - { url = "https://files.pythonhosted.org/packages/8f/85/315b45ce4b6ddc7d7fceca24068543b02bdc8782942f4ee49d652e2cc89f/jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a", size = 386502, upload-time = "2025-11-09T20:48:21.543Z" }, - { url = "https://files.pythonhosted.org/packages/74/0b/ce0434fb40c5b24b368fe81b17074d2840748b4952256bab451b72290a49/jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67", size = 519845, upload-time = "2025-11-09T20:48:22.964Z" }, - { url = "https://files.pythonhosted.org/packages/e8/a3/7a7a4488ba052767846b9c916d208b3ed114e3eb670ee984e4c565b9cf0d/jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b", size = 510701, upload-time = "2025-11-09T20:48:24.483Z" }, - { url = "https://files.pythonhosted.org/packages/c3/16/052ffbf9d0467b70af24e30f91e0579e13ded0c17bb4a8eb2aed3cb60131/jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42", size = 205029, upload-time = "2025-11-09T20:48:25.749Z" }, - { url = "https://files.pythonhosted.org/packages/e4/18/3cf1f3f0ccc789f76b9a754bdb7a6977e5d1d671ee97a9e14f7eb728d80e/jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf", size = 204960, upload-time = "2025-11-09T20:48:27.415Z" }, - { url = "https://files.pythonhosted.org/packages/02/68/736821e52ecfdeeb0f024b8ab01b5a229f6b9293bbdb444c27efade50b0f/jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451", size = 185529, upload-time = "2025-11-09T20:48:29.125Z" }, - { url = "https://files.pythonhosted.org/packages/30/61/12ed8ee7a643cce29ac97c2281f9ce3956eb76b037e88d290f4ed0d41480/jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7", size = 318974, upload-time = "2025-11-09T20:48:30.87Z" }, - { url = "https://files.pythonhosted.org/packages/2d/c6/f3041ede6d0ed5e0e79ff0de4c8f14f401bbf196f2ef3971cdbe5fd08d1d/jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684", size = 345932, upload-time = "2025-11-09T20:48:32.658Z" }, - { url = "https://files.pythonhosted.org/packages/d5/5d/4d94835889edd01ad0e2dbfc05f7bdfaed46292e7b504a6ac7839aa00edb/jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c", size = 367243, upload-time = "2025-11-09T20:48:34.093Z" }, - { url = "https://files.pythonhosted.org/packages/fd/76/0051b0ac2816253a99d27baf3dda198663aff882fa6ea7deeb94046da24e/jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d", size = 479315, upload-time = "2025-11-09T20:48:35.507Z" }, - { url = "https://files.pythonhosted.org/packages/70/ae/83f793acd68e5cb24e483f44f482a1a15601848b9b6f199dacb970098f77/jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993", size = 380714, upload-time = "2025-11-09T20:48:40.014Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/4808a88338ad2c228b1126b93fcd8ba145e919e886fe910d578230dabe3b/jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f", size = 365168, upload-time = "2025-11-09T20:48:41.462Z" }, - { url = "https://files.pythonhosted.org/packages/0c/d4/04619a9e8095b42aef436b5aeb4c0282b4ff1b27d1db1508df9f5dc82750/jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783", size = 387893, upload-time = "2025-11-09T20:48:42.921Z" }, - { url = "https://files.pythonhosted.org/packages/17/ea/d3c7e62e4546fdc39197fa4a4315a563a89b95b6d54c0d25373842a59cbe/jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b", size = 520828, upload-time = "2025-11-09T20:48:44.278Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0b/c6d3562a03fd767e31cb119d9041ea7958c3c80cb3d753eafb19b3b18349/jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6", size = 511009, upload-time = "2025-11-09T20:48:45.726Z" }, - { url = "https://files.pythonhosted.org/packages/aa/51/2cb4468b3448a8385ebcd15059d325c9ce67df4e2758d133ab9442b19834/jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183", size = 205110, upload-time = "2025-11-09T20:48:47.033Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c5/ae5ec83dec9c2d1af805fd5fe8f74ebded9c8670c5210ec7820ce0dbeb1e/jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873", size = 205223, upload-time = "2025-11-09T20:48:49.076Z" }, - { url = "https://files.pythonhosted.org/packages/97/9a/3c5391907277f0e55195550cf3fa8e293ae9ee0c00fb402fec1e38c0c82f/jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473", size = 185564, upload-time = "2025-11-09T20:48:50.376Z" }, - { url = "https://files.pythonhosted.org/packages/fe/54/5339ef1ecaa881c6948669956567a64d2670941925f245c434f494ffb0e5/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:4739a4657179ebf08f85914ce50332495811004cc1747852e8b2041ed2aab9b8", size = 311144, upload-time = "2025-11-09T20:49:10.503Z" }, - { url = "https://files.pythonhosted.org/packages/27/74/3446c652bffbd5e81ab354e388b1b5fc1d20daac34ee0ed11ff096b1b01a/jiter-0.12.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:41da8def934bf7bec16cb24bd33c0ca62126d2d45d81d17b864bd5ad721393c3", size = 305877, upload-time = "2025-11-09T20:49:12.269Z" }, - { url = "https://files.pythonhosted.org/packages/a1/f4/ed76ef9043450f57aac2d4fbeb27175aa0eb9c38f833be6ef6379b3b9a86/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c44ee814f499c082e69872d426b624987dbc5943ab06e9bbaa4f81989fdb79e", size = 340419, upload-time = "2025-11-09T20:49:13.803Z" }, - { url = "https://files.pythonhosted.org/packages/21/01/857d4608f5edb0664aa791a3d45702e1a5bcfff9934da74035e7b9803846/jiter-0.12.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd2097de91cf03eaa27b3cbdb969addf83f0179c6afc41bbc4513705e013c65d", size = 347212, upload-time = "2025-11-09T20:49:15.643Z" }, - { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974, upload-time = "2025-11-09T20:49:17.187Z" }, - { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233, upload-time = "2025-11-09T20:49:18.734Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537, upload-time = "2025-11-09T20:49:20.317Z" }, - { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, -] - -[[package]] -name = "marshmallow" -version = "4.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/61/9540e1b120e9eff74b24def6060b79347f2b4083dddbd5a435f6e8d74b14/marshmallow-4.2.1.tar.gz", hash = "sha256:4d1d66189c8d279ca73a6b0599d74117e5f8a3830b5cd766b75c2bb08e3464e7", size = 221309, upload-time = "2026-01-23T13:00:24.841Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/41/e8cfdebc3a475c397e074ceaa12e86dca1b23e8e10c2ab4d4875f9a01418/marshmallow-4.2.1-py3-none-any.whl", hash = "sha256:d82b1a83cfbb4667d050850fbed4e9d4435576cb95f5ff37894f375dce201768", size = 48451, upload-time = "2026-01-23T13:00:23.009Z" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "modal" -version = "1.3.0.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "cbor2" }, - { name = "certifi" }, - { name = "click" }, - { name = "grpclib" }, - { name = "protobuf" }, - { name = "rich" }, - { name = "synchronicity" }, - { name = "toml" }, - { name = "typer" }, - { name = "types-certifi" }, - { name = "types-toml" }, - { name = "typing-extensions" }, - { name = "watchfiles" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3f/28/4a6befa42cfa62eedb572b0bbc26edb76b664bf546ee1f9fca566448da8d/modal-1.3.0.post1.tar.gz", hash = "sha256:e86b62c6cfd5c4b40fdf7bcf078d24e4ed7730d2d0b7c70dc3225a36e85067a4", size = 649508, upload-time = "2025-12-20T02:47:56.237Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/87/3c4c9136c2d553307d8547e39bb2ed755e165b8a8272b6990af9912fef5c/modal-1.3.0.post1-py3-none-any.whl", hash = "sha256:98d338aade676bafd7c80645ef7b4302a6a7219338124ef8b210854a043ec7ed", size = 744342, upload-time = "2025-12-20T02:47:53.726Z" }, -] - -[[package]] -name = "multidict" -version = "6.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" }, - { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" }, - { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" }, - { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" }, - { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" }, - { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" }, - { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" }, - { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" }, - { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" }, - { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" }, - { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" }, - { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" }, - { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" }, - { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" }, - { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" }, - { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" }, - { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" }, - { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" }, - { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" }, - { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" }, - { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" }, - { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" }, - { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" }, - { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" }, - { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" }, - { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" }, - { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" }, - { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" }, - { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" }, - { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" }, - { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" }, - { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" }, - { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" }, - { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" }, - { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" }, - { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" }, - { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" }, - { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" }, - { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" }, - { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" }, - { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" }, - { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" }, - { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" }, - { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" }, - { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" }, - { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" }, - { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" }, - { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" }, - { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" }, - { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" }, - { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" }, - { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" }, - { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" }, - { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" }, - { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" }, - { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" }, - { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" }, - { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" }, - { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" }, - { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" }, - { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" }, - { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" }, - { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" }, - { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" }, - { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" }, - { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" }, - { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" }, - { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" }, - { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" }, - { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" }, - { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" }, - { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" }, - { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" }, - { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" }, - { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" }, - { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" }, - { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" }, - { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" }, - { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" }, - { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" }, - { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" }, - { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" }, - { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" }, - { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" }, - { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" }, - { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" }, - { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" }, - { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" }, - { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" }, - { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" }, - { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" }, - { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" }, - { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" }, - { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, -] - -[[package]] -name = "multipart" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/c9/c6f5ab81bae667d4fe42a58df29f4c2db6ad8377cfd0e9baa729e4fa3ebb/multipart-1.3.0.tar.gz", hash = "sha256:a46bd6b0eb4c1ba865beb88ddd886012a3da709b6e7b86084fc37e99087e5cf1", size = 38816, upload-time = "2025-07-26T15:09:38.056Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/d6/d547a7004b81fa0b2aafa143b09196f6635e4105cd9d2c641fa8a4051c05/multipart-1.3.0-py3-none-any.whl", hash = "sha256:439bf4b00fd7cb2dbff08ae13f49f4f49798931ecd8d496372c63537fa19f304", size = 14938, upload-time = "2025-07-26T15:09:36.884Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, -] - -[[package]] -name = "obstore" -version = "0.8.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/8c/9ec984edd0f3b72226adfaa19b1c61b15823b35b52f311ca4af36d009d15/obstore-0.8.2.tar.gz", hash = "sha256:a467bc4e97169e2ba749981b4fd0936015428d9b8f3fb83a5528536b1b6f377f", size = 168852, upload-time = "2025-09-16T15:34:55.786Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/c4/018f90701f1e5ea3fbd57f61463f42e1ef5218e548d3adcf12b6be021c34/obstore-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2edaa97687c191c5324bb939d72f6fe86a7aa8191c410f1648c14e8296d05c1c", size = 3622568, upload-time = "2025-09-16T15:33:14.196Z" }, - { url = "https://files.pythonhosted.org/packages/a8/62/72dd1e7d52fc554bb1fdb1a9499bda219cf3facea5865a1d97fdc00b3a1b/obstore-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c4fb7ef8108f08d14edc8bec9e9a6a2e5c4d14eddb8819f5d0da498aff6e8888", size = 3356109, upload-time = "2025-09-16T15:33:15.315Z" }, - { url = "https://files.pythonhosted.org/packages/e0/ae/089fe5b9207091252fe5ce352551214f04560f85eb8f2cc4f716a6a1a57e/obstore-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fda8f658c0edf799ab1e264f9b12c7c184cd09a5272dc645d42e987810ff2772", size = 3454588, upload-time = "2025-09-16T15:33:16.421Z" }, - { url = "https://files.pythonhosted.org/packages/ea/10/1865ae2d1ba45e8ae85fb0c1aada2dc9533baf60c4dfe74dab905348d74a/obstore-0.8.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87fe2bc15ce4051ecb56abd484feca323c2416628beb62c1c7b6712114564d6e", size = 3688627, upload-time = "2025-09-16T15:33:17.604Z" }, - { url = "https://files.pythonhosted.org/packages/a6/09/5d7ba6d0aeac563ea5f5586401c677bace4f782af83522b1fdf15430e152/obstore-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2482aa2562ab6a4ca40250b26bea33f8375b59898a9b5615fd412cab81098123", size = 3959896, upload-time = "2025-09-16T15:33:18.789Z" }, - { url = "https://files.pythonhosted.org/packages/16/15/2b3eda59914761a9ff4d840e2daec5697fd29b293bd18d3dc11c593aed06/obstore-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4153b928f5d2e9c6cb645e83668a53e0b42253d1e8bcb4e16571fc0a1434599a", size = 3933162, upload-time = "2025-09-16T15:33:19.935Z" }, - { url = "https://files.pythonhosted.org/packages/14/7a/5fc63b41526587067537fb1498c59a210884664c65ccf0d1f8f823b0875a/obstore-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbfa9c38620cc191be98c8b5558c62071e495dc6b1cc724f38293ee439aa9f92", size = 3769605, upload-time = "2025-09-16T15:33:21.389Z" }, - { url = "https://files.pythonhosted.org/packages/77/4e/2208ab6e1fc021bf8b7e117249a10ab75d0ed24e0f2de1a8d7cd67d885b5/obstore-0.8.2-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:0822836eae8d52499f10daef17f26855b4c123119c6eb984aa4f2d525ec2678d", size = 3534396, upload-time = "2025-09-16T15:33:22.574Z" }, - { url = "https://files.pythonhosted.org/packages/1d/8f/a0e2882edd6bd285c82b8a5851c4ecf386c93fe75b6e340d5d9d30e809fc/obstore-0.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ef6435dfd586d83b4f778e7927a5d5b0d8b771e9ba914bc809a13d7805410e6", size = 3697777, upload-time = "2025-09-16T15:33:23.723Z" }, - { url = "https://files.pythonhosted.org/packages/94/78/ebf0c33bed5c9a8eed3b00eefafbcc0a687eeb1e05451c76fcf199d29ff8/obstore-0.8.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0f2cba91f4271ca95a932a51aa8dda1537160342b33f7836c75e1eb9d40621a2", size = 3681546, upload-time = "2025-09-16T15:33:24.935Z" }, - { url = "https://files.pythonhosted.org/packages/af/21/9bf4fb9e53fd5f01af580b6538de2eae857e31d24b0ebfc4d916c306a1e4/obstore-0.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:23c876d603af0627627808d19a58d43eb5d8bfd02eecd29460bc9a58030fed55", size = 3765336, upload-time = "2025-09-16T15:33:26.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3c/7f6895c23719482d231b2d6ed328e3223fdf99785f6850fba8d2fc5a86ee/obstore-0.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ff3c4b5d07629b70b9dee494cd6b94fff8465c3864752181a1cb81a77190fe42", size = 3941142, upload-time = "2025-09-16T15:33:27.275Z" }, - { url = "https://files.pythonhosted.org/packages/93/a4/56ccdb756161595680a28f4b0def2c04f7048ffacf128029be8394367b26/obstore-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:aadb2cb72de7227d07f4570f82729625ffc77522fadca5cf13c3a37fbe8c8de9", size = 3970172, upload-time = "2025-09-16T15:33:28.393Z" }, - { url = "https://files.pythonhosted.org/packages/2b/dc/60fefbb5736e69eab56657bca04ca64dc07fdeccb3814164a31b62ad066b/obstore-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bb70ce297a47392b1d9a3e310f18d59cd5ebbb9453428210fef02ed60e4d75d1", size = 3612955, upload-time = "2025-09-16T15:33:29.527Z" }, - { url = "https://files.pythonhosted.org/packages/d2/8b/844e8f382e5a12b8a3796a05d76a03e12c7aedc13d6900419e39207d7868/obstore-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1619bf618428abf1f607e0b219b2e230a966dcf697b717deccfa0983dd91f646", size = 3346564, upload-time = "2025-09-16T15:33:30.698Z" }, - { url = "https://files.pythonhosted.org/packages/89/73/8537f99e09a38a54a6a15ede907aa25d4da089f767a808f0b2edd9c03cec/obstore-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4605c3ed7c9515aeb4c619b5f7f2c9986ed4a79fe6045e536b5e59b804b1476", size = 3460809, upload-time = "2025-09-16T15:33:31.837Z" }, - { url = "https://files.pythonhosted.org/packages/b4/99/7714dec721e43f521d6325a82303a002cddad089437640f92542b84e9cc8/obstore-0.8.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce42670417876dd8668cbb8659e860e9725e5f26bbc86449fd259970e2dd9d18", size = 3692081, upload-time = "2025-09-16T15:33:33.028Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bd/4ac4175fe95a24c220a96021c25c432bcc0c0212f618be0737184eebbaad/obstore-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a3e893b2a06585f651c541c1972fe1e3bf999ae2a5fda052ee55eb7e6516f5", size = 3957466, upload-time = "2025-09-16T15:33:34.528Z" }, - { url = "https://files.pythonhosted.org/packages/4e/04/caa288fb735484fc5cb019bdf3d896eaccfae0ac4622e520d05692c46790/obstore-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08462b32f95a9948ed56ed63e88406e2e5a4cae1fde198f9682e0fb8487100ed", size = 3951293, upload-time = "2025-09-16T15:33:35.733Z" }, - { url = "https://files.pythonhosted.org/packages/44/2f/d380239da2d6a1fda82e17df5dae600a404e8a93a065784518ff8325d5f6/obstore-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a0bf7763292a8fc47d01cd66e6f19002c5c6ad4b3ed4e6b2729f5e190fa8a0d", size = 3766199, upload-time = "2025-09-16T15:33:36.904Z" }, - { url = "https://files.pythonhosted.org/packages/28/41/d391be069d3da82969b54266948b2582aeca5dd735abeda4d63dba36e07b/obstore-0.8.2-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:bcd47f8126cb192cbe86942b8f73b1c45a651ce7e14c9a82c5641dfbf8be7603", size = 3529678, upload-time = "2025-09-16T15:33:38.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/4c/4862fdd1a3abde459ee8eea699b1797df638a460af235b18ca82c8fffb72/obstore-0.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57eda9fd8c757c3b4fe36cf3918d7e589cc1286591295cc10b34122fa36dd3fd", size = 3698079, upload-time = "2025-09-16T15:33:39.696Z" }, - { url = "https://files.pythonhosted.org/packages/68/ca/014e747bc53b570059c27e3565b2316fbe5c107d4134551f4cd3e24aa667/obstore-0.8.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ea44442aad8992166baa69f5069750979e4c5d9ffce772e61565945eea5774b9", size = 3687154, upload-time = "2025-09-16T15:33:40.92Z" }, - { url = "https://files.pythonhosted.org/packages/6f/89/6db5f8edd93028e5b8bfbeee15e6bd3e56f72106107d31cb208b57659de4/obstore-0.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:41496a3ab8527402db4142aaaf0d42df9d7d354b13ba10d9c33e0e48dd49dd96", size = 3773444, upload-time = "2025-09-16T15:33:42.123Z" }, - { url = "https://files.pythonhosted.org/packages/26/e5/c9e2cc540689c873beb61246e1615d6e38301e6a34dec424f5a5c63c1afd/obstore-0.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43da209803f052df96c7c3cbec512d310982efd2407e4a435632841a51143170", size = 3939315, upload-time = "2025-09-16T15:33:43.252Z" }, - { url = "https://files.pythonhosted.org/packages/4d/c9/bb53280ca50103c1ffda373cdc9b0f835431060039c2897cbc87ddd92e42/obstore-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:1836f5dcd49f9f2950c75889ab5c51fb290d3ea93cdc39a514541e0be3af016e", size = 3978234, upload-time = "2025-09-16T15:33:44.393Z" }, - { url = "https://files.pythonhosted.org/packages/f0/5d/8c3316cc958d386d5e6ab03e9db9ddc27f8e2141cee4a6777ae5b92f3aac/obstore-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:212f033e53fe6e53d64957923c5c88949a400e9027f7038c705ec2e9038be563", size = 3612027, upload-time = "2025-09-16T15:33:45.6Z" }, - { url = "https://files.pythonhosted.org/packages/ea/4d/699359774ce6330130536d008bfc32827fab0c25a00238d015a5974a3d1d/obstore-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bee21fa4ba148d08fa90e47a96df11161661ed31e09c056a373cb2154b0f2852", size = 3344686, upload-time = "2025-09-16T15:33:47.185Z" }, - { url = "https://files.pythonhosted.org/packages/82/37/55437341f10512906e02fd9fa69a8a95ad3f2f6a916d3233fda01763d110/obstore-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c66594b59832ff1ced4c72575d9beb8b5f9b4e404ac1150a42bfb226617fd50", size = 3459860, upload-time = "2025-09-16T15:33:48.382Z" }, - { url = "https://files.pythonhosted.org/packages/7a/51/4245a616c94ee4851965e33f7a563ab4090cc81f52cc73227ff9ceca2e46/obstore-0.8.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:089f33af5c2fe132d00214a0c1f40601b28f23a38e24ef9f79fb0576f2730b74", size = 3691648, upload-time = "2025-09-16T15:33:49.524Z" }, - { url = "https://files.pythonhosted.org/packages/4e/f1/4e2fb24171e3ca3641a4653f006be826e7e17634b11688a5190553b00b83/obstore-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d87f658dfd340d5d9ea2d86a7c90d44da77a0db9e00c034367dca335735110cf", size = 3956867, upload-time = "2025-09-16T15:33:51.082Z" }, - { url = "https://files.pythonhosted.org/packages/42/f5/b703115361c798c9c1744e1e700d5908d904a8c2e2bd38bec759c9ffb469/obstore-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2e4fa92828c4fbc2d487f3da2d3588701a1b67d9f6ca3c97cc2afc912e9c63", size = 3950599, upload-time = "2025-09-16T15:33:52.173Z" }, - { url = "https://files.pythonhosted.org/packages/53/20/08c6dc0f20c1394e2324b9344838e4e7af770cdcb52c30757a475f50daeb/obstore-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab440e89c5c37a8ec230857dd65147d4b923e0cada33297135d05e0f937d696a", size = 3765865, upload-time = "2025-09-16T15:33:53.291Z" }, - { url = "https://files.pythonhosted.org/packages/77/20/77907765e29b2eba6bd8821872284d91170d7084f670855b2dfcb249ea14/obstore-0.8.2-cp313-cp313-manylinux_2_24_aarch64.whl", hash = "sha256:b9beed107c5c9cd995d4a73263861fcfbc414d58773ed65c14f80eb18258a932", size = 3529807, upload-time = "2025-09-16T15:33:54.535Z" }, - { url = "https://files.pythonhosted.org/packages/a5/f5/f629d39cc30d050f52b1bf927e4d65c1cc7d7ffbb8a635cd546b5c5219a0/obstore-0.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b75b4e7746292c785e31edcd5aadc8b758238372a19d4c5e394db5c305d7d175", size = 3693629, upload-time = "2025-09-16T15:33:56.016Z" }, - { url = "https://files.pythonhosted.org/packages/30/ff/106763fd10f2a1cb47f2ef1162293c78ad52f4e73223d8d43fc6b755445d/obstore-0.8.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f33e6c366869d05ab0b7f12efe63269e631c5450d95d6b4ba4c5faf63f69de70", size = 3686176, upload-time = "2025-09-16T15:33:57.247Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0c/d2ccb6f32feeca906d5a7c4255340df5262af8838441ca06c9e4e37b67d5/obstore-0.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:12c885a9ce5ceb09d13cc186586c0c10b62597eff21b985f6ce8ff9dab963ad3", size = 3773081, upload-time = "2025-09-16T15:33:58.475Z" }, - { url = "https://files.pythonhosted.org/packages/fa/79/40d1cc504cefc89c9b3dd8874287f3fddc7d963a8748d6dffc5880222013/obstore-0.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4accc883b93349a81c9931e15dd318cc703b02bbef2805d964724c73d006d00e", size = 3938589, upload-time = "2025-09-16T15:33:59.734Z" }, - { url = "https://files.pythonhosted.org/packages/14/dd/916c6777222db3271e9fb3cf9a97ed92b3a9b3e465bdeec96de9ab809d53/obstore-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ec850adf9980e5788a826ccfd5819989724e2a2f712bfa3258e85966c8d9981e", size = 3977768, upload-time = "2025-09-16T15:34:01.25Z" }, - { url = "https://files.pythonhosted.org/packages/f1/61/66f8dc98bbf5613bbfe5bf21747b4c8091442977f4bd897945895ab7325c/obstore-0.8.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1431e40e9bb4773a261e51b192ea6489d0799b9d4d7dbdf175cdf813eb8c0503", size = 3623364, upload-time = "2025-09-16T15:34:02.957Z" }, - { url = "https://files.pythonhosted.org/packages/1a/66/6d527b3027e42f625c8fc816ac7d19b0d6228f95bfe7666e4d6b081d2348/obstore-0.8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ddb39d4da303f50b959da000aa42734f6da7ac0cc0be2d5a7838b62c97055bb9", size = 3347764, upload-time = "2025-09-16T15:34:04.236Z" }, - { url = "https://files.pythonhosted.org/packages/0d/79/c00103302b620192ea447a948921ad3fed031ce3d19e989f038e1183f607/obstore-0.8.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e01f4e13783db453e17e005a4a3ceff09c41c262e44649ba169d253098c775e8", size = 3460981, upload-time = "2025-09-16T15:34:05.595Z" }, - { url = "https://files.pythonhosted.org/packages/3d/d9/bfe4ed4b1aebc45b56644dd5b943cf8e1673505cccb352e66878a457e807/obstore-0.8.2-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df0fc2d0bc17caff9b538564ddc26d7616f7e8b7c65b1a3c90b5048a8ad2e797", size = 3692711, upload-time = "2025-09-16T15:34:06.796Z" }, - { url = "https://files.pythonhosted.org/packages/13/47/cd6c2cbb18e1f40c77e7957a4a03d2d83f1859a2e876a408f1ece81cad4c/obstore-0.8.2-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e439d06c99a140348f046c9f598ee349cc2dcd9105c15540a4b231f9cc48bbae", size = 3958362, upload-time = "2025-09-16T15:34:08.277Z" }, - { url = "https://files.pythonhosted.org/packages/3d/ea/5ee82bf23abd71c7d6a3f2d008197ae8f8f569d41314c26a8f75318245be/obstore-0.8.2-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e37d9046669fcc59522d0faf1d105fcbfd09c84cccaaa1e809227d8e030f32c", size = 3957082, upload-time = "2025-09-16T15:34:09.477Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ee/46650405e50fdaa8d95f30375491f9c91fac9517980e8a28a4a6af66927f/obstore-0.8.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2646fdcc4bbe92dc2bb5bcdff15574da1211f5806c002b66d514cee2a23c7cb8", size = 3775539, upload-time = "2025-09-16T15:34:10.726Z" }, - { url = "https://files.pythonhosted.org/packages/35/d6/348a7ebebe2ca3d94dfc75344ea19675ae45472823e372c1852844078307/obstore-0.8.2-cp314-cp314-manylinux_2_24_aarch64.whl", hash = "sha256:e31a7d37675056d93dfc244605089dee67f5bba30f37c88436623c8c5ad9ba9d", size = 3535048, upload-time = "2025-09-16T15:34:12.076Z" }, - { url = "https://files.pythonhosted.org/packages/41/07/b7a16cc0da91a4b902d47880ad24016abfe7880c63f7cdafda45d89a2f91/obstore-0.8.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:656313dd8170dde0f0cd471433283337a63912e8e790a121f7cc7639c83e3816", size = 3699035, upload-time = "2025-09-16T15:34:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/7f/74/3269a3a58347e0b019742d888612c4b765293c9c75efa44e144b1e884c0d/obstore-0.8.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329038c9645d6d1741e77fe1a53e28a14b1a5c1461cfe4086082ad39ebabf981", size = 3687307, upload-time = "2025-09-16T15:34:14.501Z" }, - { url = "https://files.pythonhosted.org/packages/01/f9/4fd4819ad6a49d2f462a45be453561f4caebded0dc40112deeffc34b89b1/obstore-0.8.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1e4df99b369790c97c752d126b286dc86484ea49bff5782843a265221406566f", size = 3776076, upload-time = "2025-09-16T15:34:16.207Z" }, - { url = "https://files.pythonhosted.org/packages/14/dd/7c4f958fa0b9fc4778fb3d232e38b37db8c6b260f641022fbba48b049d7e/obstore-0.8.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9e1c65c65e20cc990414a8a9af88209b1bbc0dd9521b5f6b0293c60e19439bb7", size = 3947445, upload-time = "2025-09-16T15:34:17.423Z" }, -] - -[[package]] -name = "openai" -version = "2.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/b1/12fe1c196bea326261718eb037307c1c1fe1dedc2d2d4de777df822e6238/openai-2.14.0.tar.gz", hash = "sha256:419357bedde9402d23bf8f2ee372fca1985a73348debba94bddff06f19459952", size = 626938, upload-time = "2025-12-19T03:28:45.742Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/4b/7c1a00c2c3fbd004253937f7520f692a9650767aa73894d7a34f0d65d3f4/openai-2.14.0-py3-none-any.whl", hash = "sha256:7ea40aca4ffc4c4a776e77679021b47eec1160e341f42ae086ba949c9dcc9183", size = 1067558, upload-time = "2025-12-19T03:28:43.727Z" }, -] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "portkey-ai" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "cached-property" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "types-requests" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d4/8a/f5bbaab806ad61d9959cb7c88c639200feacac1b2ba7b455b97a2f216e7c/portkey_ai-2.1.0.tar.gz", hash = "sha256:c2558041c568eef8528737978089301cb9be056f166a683251831cbfa6a623cb", size = 567417, upload-time = "2025-11-25T20:32:43.102Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/11/c585b90ac842027e5f4f7f7cee72d3197f58ff24b6d7c5f1243aa8fa96be/portkey_ai-2.1.0-py3-none-any.whl", hash = "sha256:2166033f8e198745947fee5321d0bbcfb005afc35468bd5a948fa83dc16b6767", size = 1181622, upload-time = "2025-11-25T20:32:41.185Z" }, -] - -[[package]] -name = "pre-commit" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, -] - -[[package]] -name = "prime-sandboxes" -version = "0.2.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiofiles" }, - { name = "httpx" }, - { name = "pydantic" }, - { name = "tenacity" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/59/55/e6c855074af9ef027f3601396d4e5e0b464f26f724c074253dd0dd2f47b7/prime_sandboxes-0.2.9.tar.gz", hash = "sha256:af4ad06d205a95f4c54778ecac8492f3f382c850528072df79252e7fd0f3fa08", size = 45538, upload-time = "2026-01-07T20:24:15.352Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/ae/5d38212ff0cbad2c468e9b5cf1e3235af04d123b55c4adaa8812abdeabbb/prime_sandboxes-0.2.9-py3-none-any.whl", hash = "sha256:dab4dd9c1f4f633386593cfb08f7a3dacb2de4eb6e75f4a2ec7f5f1c6ea2207b", size = 18824, upload-time = "2026-01-07T20:24:14.133Z" }, -] - -[[package]] -name = "propcache" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, - { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, - { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, - { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, - { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, - { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, - { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, - { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, - { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, - { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, - { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, - { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, - { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, - { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, - { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, - { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, - { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, - { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, - { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, - { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, - { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, - { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, - { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, - { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, - { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, -] - -[[package]] -name = "protobuf" -version = "6.33.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/44/e49ecff446afeec9d1a66d6bbf9adc21e3c7cea7803a920ca3773379d4f6/protobuf-6.33.2.tar.gz", hash = "sha256:56dc370c91fbb8ac85bc13582c9e373569668a290aa2e66a590c2a0d35ddb9e4", size = 444296, upload-time = "2025-12-06T00:17:53.311Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/91/1e3a34881a88697a7354ffd177e8746e97a722e5e8db101544b47e84afb1/protobuf-6.33.2-cp310-abi3-win32.whl", hash = "sha256:87eb388bd2d0f78febd8f4c8779c79247b26a5befad525008e49a6955787ff3d", size = 425603, upload-time = "2025-12-06T00:17:41.114Z" }, - { url = "https://files.pythonhosted.org/packages/64/20/4d50191997e917ae13ad0a235c8b42d8c1ab9c3e6fd455ca16d416944355/protobuf-6.33.2-cp310-abi3-win_amd64.whl", hash = "sha256:fc2a0e8b05b180e5fc0dd1559fe8ebdae21a27e81ac77728fb6c42b12c7419b4", size = 436930, upload-time = "2025-12-06T00:17:43.278Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ca/7e485da88ba45c920fb3f50ae78de29ab925d9e54ef0de678306abfbb497/protobuf-6.33.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d9b19771ca75935b3a4422957bc518b0cecb978b31d1dd12037b088f6bcc0e43", size = 427621, upload-time = "2025-12-06T00:17:44.445Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4f/f743761e41d3b2b2566748eb76bbff2b43e14d5fcab694f494a16458b05f/protobuf-6.33.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5d3b5625192214066d99b2b605f5783483575656784de223f00a8d00754fc0e", size = 324460, upload-time = "2025-12-06T00:17:45.678Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fa/26468d00a92824020f6f2090d827078c09c9c587e34cbfd2d0c7911221f8/protobuf-6.33.2-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8cd7640aee0b7828b6d03ae518b5b4806fdfc1afe8de82f79c3454f8aef29872", size = 339168, upload-time = "2025-12-06T00:17:46.813Z" }, - { url = "https://files.pythonhosted.org/packages/56/13/333b8f421738f149d4fe5e49553bc2a2ab75235486259f689b4b91f96cec/protobuf-6.33.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:1f8017c48c07ec5859106533b682260ba3d7c5567b1ca1f24297ce03384d1b4f", size = 323270, upload-time = "2025-12-06T00:17:48.253Z" }, - { url = "https://files.pythonhosted.org/packages/0e/15/4f02896cc3df04fc465010a4c6a0cd89810f54617a32a70ef531ed75d61c/protobuf-6.33.2-py3-none-any.whl", hash = "sha256:7636aad9bb01768870266de5dc009de2d1b936771b38a793f73cbbf279c91c5c", size = 170501, upload-time = "2025-12-06T00:17:52.211Z" }, -] - -[[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, upload-time = "2024-09-10T22:41:42.55Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135, upload-time = "2024-09-11T16:00:36.122Z" }, -] - -[[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, upload-time = "2025-03-28T02:41:22.17Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, -] - -[[package]] -name = "pydantic" -version = "2.12.5" -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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, -] - -[[package]] -name = "pydantic-core" -version = "2.41.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, - { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, - { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, - { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, - { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, - { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, - { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, - { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, - { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, - { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, - { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, - { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, - { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, - { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, - { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, - { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, - { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, - { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, - { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, - { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, - { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, -] - -[[package]] -name = "pytest-cov" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-dotenv" -version = "1.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, - { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, - { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, - { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, - { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, - { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, - { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, - { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, - { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - -[[package]] -name = "rich" -version = "14.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, -] - -[[package]] -name = "rlm" -version = "0.1.0" -source = { editable = "." } -dependencies = [ - { name = "anthropic" }, - { name = "google-genai" }, - { name = "openai" }, - { name = "portkey-ai" }, - { name = "pytest" }, - { name = "python-dotenv" }, - { name = "requests" }, - { name = "rich" }, -] - -[package.optional-dependencies] -daytona = [ - { name = "daytona" }, - { name = "dill" }, -] -modal = [ - { name = "dill" }, - { name = "modal" }, -] -prime = [ - { name = "dill" }, - { name = "prime-sandboxes" }, -] - -[package.dev-dependencies] -dev = [ - { name = "pre-commit" }, - { name = "ruff" }, - { name = "ty" }, -] -test = [ - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, -] - -[package.metadata] -requires-dist = [ - { name = "anthropic", specifier = ">=0.75.0" }, - { name = "daytona", marker = "extra == 'daytona'", specifier = ">=0.128.1" }, - { name = "dill", marker = "extra == 'daytona'", specifier = ">=0.3.7" }, - { name = "dill", marker = "extra == 'modal'", specifier = ">=0.3.7" }, - { name = "dill", marker = "extra == 'prime'", specifier = ">=0.3.7" }, - { name = "google-genai", specifier = ">=1.56.0" }, - { name = "modal", marker = "extra == 'modal'", specifier = ">=0.73.0" }, - { name = "openai", specifier = ">=2.14.0" }, - { name = "portkey-ai", specifier = ">=2.1.0" }, - { name = "prime-sandboxes", marker = "extra == 'prime'", specifier = ">=0.2.0" }, - { name = "pytest", specifier = ">=9.0.2" }, - { name = "python-dotenv", specifier = ">=1.2.1" }, - { name = "requests", specifier = ">=2.32.5" }, - { name = "rich", specifier = ">=13.0.0" }, -] -provides-extras = ["modal", "daytona", "prime"] - -[package.metadata.requires-dev] -dev = [ - { name = "pre-commit", specifier = ">=4.5.1" }, - { name = "ruff", specifier = ">=0.14.10" }, - { name = "ty", specifier = ">=0.0.7" }, -] -test = [ - { name = "pytest", specifier = ">=8.0.0" }, - { name = "pytest-asyncio", specifier = ">=0.24.0" }, - { name = "pytest-cov", specifier = ">=6.0.0" }, -] - -[[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, upload-time = "2025-04-16T09:51:18.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, -] - -[[package]] -name = "ruff" -version = "0.14.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, - { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" }, - { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" }, - { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" }, - { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" }, - { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" }, - { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" }, - { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" }, - { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" }, - { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" }, - { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" }, - { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" }, - { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" }, - { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" }, - { url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" }, - { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[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, upload-time = "2024-02-25T23:20:04.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, -] - -[[package]] -name = "synchronicity" -version = "0.11.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/26/8874d34755691994266d4a844ba8d53d10c2690ec67f246ca4d6b6f34cbb/synchronicity-0.11.1.tar.gz", hash = "sha256:3628df9ab34bd7be89b729104114841c62612c5d5ec43b76f4b7b243185ec1a8", size = 58131, upload-time = "2025-12-19T18:28:42.291Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/b9/71153db12f4ad029cfe9b7fbf9792ef3fc9ade4485d31a13470b52954e62/synchronicity-0.11.1-py3-none-any.whl", hash = "sha256:53959c7f8b9b852fb5ea4d3d290a47a04310ede483a4cf0f8452cb4b5fa09db2", size = 40399, upload-time = "2025-12-19T18:28:40.972Z" }, -] - -[[package]] -name = "tenacity" -version = "9.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, -] - -[[package]] -name = "toml" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, -] - -[[package]] -name = "tomli" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, - { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, - { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, - { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, - { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, - { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, - { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, - { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, - { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, - { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, - { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, - { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, - { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, - { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, - { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, - { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, - { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, - { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, - { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, - { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, - { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, - { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, - { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, - { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, - { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, - { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, - { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, - { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, - { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, - { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, - { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, - { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, - { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, - { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, - { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, -] - -[[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, upload-time = "2024-11-24T20:12:22.481Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, -] - -[[package]] -name = "ty" -version = "0.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/43/8be3ec2e2ce6119cff9ee3a207fae0cb4f2b4f8ed6534175130a32be24a7/ty-0.0.7.tar.gz", hash = "sha256:90e53b20b86c418ee41a8385f17da44cc7f916f96f9eee87593423ce8292ca72", size = 4826677, upload-time = "2025-12-24T21:28:49.136Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/56/fafa123acf955089306372add312f16e97aba61f7c4daf74e2bb9c350d23/ty-0.0.7-py3-none-linux_armv6l.whl", hash = "sha256:b30105bd9a0b064497111c50c206d5b6a032f29bcf39f09a12085c3009d72784", size = 9862360, upload-time = "2025-12-24T21:28:36.762Z" }, - { url = "https://files.pythonhosted.org/packages/71/f4/9c30ff498d9a60e24f16d26c0cf93cd03a119913ffa720a77149f02df06e/ty-0.0.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b4df20889115f3d5611a9d9cdedc222e3fd82b5fe87bb0a9f7246e53a23becc7", size = 9712866, upload-time = "2025-12-24T21:28:25.926Z" }, - { url = "https://files.pythonhosted.org/packages/43/84/e06a4a6e4011890027ffee41efbf261b1335103d09009d625ace7f1a60eb/ty-0.0.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f699589d8511e1e17c5a7edfc5f4a4e80f2a6d4a3932a0e9e3422fd32d731472", size = 9221692, upload-time = "2025-12-24T21:28:29.649Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e9/ebb4192d3627730125d40ee403a17dc91bab59d69c3eff286453b3218d01/ty-0.0.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3eaec2d8aa153ee4bcc43b17a384d0f9e66177c8c8127be3358b6b8348b9e3b", size = 9710340, upload-time = "2025-12-24T21:28:55.148Z" }, - { url = "https://files.pythonhosted.org/packages/8f/4a/ec144458a9cfb324d5cb471483094e62e74d73179343dff262a5cca1a1e1/ty-0.0.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:177d160295e6a56bdf0b61f6120bc4502fff301d4d10855ba711c109aa7f37fb", size = 9670317, upload-time = "2025-12-24T21:28:43.096Z" }, - { url = "https://files.pythonhosted.org/packages/b6/94/fe7106fd5e2ac06b81fba7b785a6216774618edc3fda9e17f58efe3cede6/ty-0.0.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30518b95ab5cc83615794cca765a5fb86df39a0d9c3dadc0ab2d787ab7830008", size = 10096517, upload-time = "2025-12-24T21:28:23.667Z" }, - { url = "https://files.pythonhosted.org/packages/45/d9/db96ccfd663c96bdd4bb63db72899198c01445012f939477a5318a563f14/ty-0.0.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7867b3f75c2d9602cc6fb3b6d462580b707c2d112d4b27037142b0d01f8bfd03", size = 10996406, upload-time = "2025-12-24T21:28:39.134Z" }, - { url = "https://files.pythonhosted.org/packages/94/da/103915c08c3e6a14f95959614646fcdc9a240cd9a039fadbdcd086c819ee/ty-0.0.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878d45858e209b7904753fbc5155f4cb75dadc20a26bbb77614bfef31580f9ae", size = 10712829, upload-time = "2025-12-24T21:28:27.745Z" }, - { url = "https://files.pythonhosted.org/packages/47/c0/d9be417bc8e459e13e9698978579eec9868f91f4c5d6ef663249967fec8b/ty-0.0.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:651820b193901825afce40ae68f6a51cd64dbfa4b81a45db90061401261f25e4", size = 10486541, upload-time = "2025-12-24T21:28:45.17Z" }, - { url = "https://files.pythonhosted.org/packages/ad/09/d1858c66620d8ae566e021ad0d7168914b1568841f8fe9e439116ce6b440/ty-0.0.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f56a5a0c1c045863b1b70c358a392b3f73b8528c5c571d409f19dd465525e116", size = 10255312, upload-time = "2025-12-24T21:28:53.17Z" }, - { url = "https://files.pythonhosted.org/packages/b6/0a/78f75089db491fd5fcc13d2845a0b2771b7f7d377450c64c6616e9c227bc/ty-0.0.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:748218fbc1f7b7f1b9d14e77d4f3d7fec72af794417e26b0185bdb94153afe1c", size = 9696201, upload-time = "2025-12-24T21:28:57.345Z" }, - { url = "https://files.pythonhosted.org/packages/01/9e/b26e94832fd563fef6f77a4487affc77a027b0e53106422c66aafb37fa01/ty-0.0.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1ff80f3985a52a7358b9069b4a8d223e92cf312544a934a062d6d3a4fb6876b3", size = 9688907, upload-time = "2025-12-24T21:28:59.485Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8f/cc48601fb92c964cf6c34277e0d947076146b7de47aa11b5dbae45e01ce7/ty-0.0.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a808910ce672ba4446699f4c021283208f58f988bcfc3bdbdfc6e005819d9ee0", size = 9829982, upload-time = "2025-12-24T21:28:34.429Z" }, - { url = "https://files.pythonhosted.org/packages/b5/af/7fa9c2bfa25865968bded637f7e71f1a712f4fbede88f487b6a9101ab936/ty-0.0.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2718fea5f314eda01703fb406ec89b1fc8710b3fc6a09bbd6f7a4f3502ddc889", size = 10361037, upload-time = "2025-12-24T21:28:47.027Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5b/1a6ff1495975cd1c02aa8d03bc5c9d8006eaeb8bf354446f88d70f0518fd/ty-0.0.7-py3-none-win32.whl", hash = "sha256:ae89bb8dc50deb66f34eab3113aa61ac5d7f85ecf16279e5918548085a89021c", size = 9295092, upload-time = "2025-12-24T21:28:51.041Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f6/47e9364635d048002354f84d2d0d6dfc9eb166dc67850739f88e1fec4fc5/ty-0.0.7-py3-none-win_amd64.whl", hash = "sha256:25bd20e3d4d0f07b422f9b42711ba24d28116031273bd23dbda66cec14df1c06", size = 10162816, upload-time = "2025-12-24T21:28:41.006Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f4/c4fc28410c4493982b7481fb23f62bacb02fd2912ebec3b9bc7de18bebb8/ty-0.0.7-py3-none-win_arm64.whl", hash = "sha256:c87d27484dba9fca0053b6a9eee47eecc760aab2bbb8e6eab3d7f81531d1ad0c", size = 9653112, upload-time = "2025-12-24T21:28:31.562Z" }, -] - -[[package]] -name = "typer" -version = "0.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/30/ff9ede605e3bd086b4dd842499814e128500621f7951ca1e5ce84bbf61b1/typer-0.21.0.tar.gz", hash = "sha256:c87c0d2b6eee3b49c5c64649ec92425492c14488096dfbc8a0c2799b2f6f9c53", size = 106781, upload-time = "2025-12-25T09:54:53.651Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e4/5ebc1899d31d2b1601b32d21cfb4bba022ae6fce323d365f0448031b1660/typer-0.21.0-py3-none-any.whl", hash = "sha256:c79c01ca6b30af9fd48284058a7056ba0d3bf5cf10d0ff3d0c5b11b68c258ac6", size = 47109, upload-time = "2025-12-25T09:54:51.918Z" }, -] - -[[package]] -name = "types-certifi" -version = "2021.10.8.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095, upload-time = "2022-06-09T15:19:05.244Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136, upload-time = "2022-06-09T15:19:03.127Z" }, -] - -[[package]] -name = "types-requests" -version = "2.32.4.20250913" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" }, -] - -[[package]] -name = "types-toml" -version = "0.10.8.20240310" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392, upload-time = "2024-03-10T02:18:37.518Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777, upload-time = "2024-03-10T02:18:36.568Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "typing-inspection" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" }, -] - -[[package]] -name = "virtualenv" -version = "20.35.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, -] - -[[package]] -name = "watchfiles" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, - { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, - { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, - { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, - { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, - { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, - { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, - { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, - { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, - { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, - { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, - { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, - { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, - { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, - { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, - { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, - { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, - { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, - { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, - { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, - { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, - { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, - { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, - { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, -] - -[[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, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { 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, upload-time = "2025-03-05T20:02:36.695Z" }, - { 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, upload-time = "2025-03-05T20:02:37.985Z" }, - { 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, upload-time = "2025-03-05T20:02:39.298Z" }, - { 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, upload-time = "2025-03-05T20:02:40.595Z" }, - { 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, upload-time = "2025-03-05T20:02:41.926Z" }, - { 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, upload-time = "2025-03-05T20:02:43.304Z" }, - { 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, upload-time = "2025-03-05T20:02:48.812Z" }, - { 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, upload-time = "2025-03-05T20:02:50.14Z" }, - { 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, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, -] - -[[package]] -name = "wrapt" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/49/2a/6de8a50cb435b7f42c46126cf1a54b2aab81784e74c8595c8e025e8f36d3/wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f", size = 82040, upload-time = "2025-11-07T00:45:33.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/60/553997acf3939079dab022e37b67b1904b5b0cc235503226898ba573b10c/wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590", size = 77480, upload-time = "2025-11-07T00:43:30.573Z" }, - { url = "https://files.pythonhosted.org/packages/2d/50/e5b3d30895d77c52105c6d5cbf94d5b38e2a3dd4a53d22d246670da98f7c/wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6", size = 60690, upload-time = "2025-11-07T00:43:31.594Z" }, - { url = "https://files.pythonhosted.org/packages/f0/40/660b2898703e5cbbb43db10cdefcc294274458c3ca4c68637c2b99371507/wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7", size = 61578, upload-time = "2025-11-07T00:43:32.918Z" }, - { url = "https://files.pythonhosted.org/packages/5b/36/825b44c8a10556957bc0c1d84c7b29a40e05fcf1873b6c40aa9dbe0bd972/wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28", size = 114115, upload-time = "2025-11-07T00:43:35.605Z" }, - { url = "https://files.pythonhosted.org/packages/83/73/0a5d14bb1599677304d3c613a55457d34c344e9b60eda8a737c2ead7619e/wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb", size = 116157, upload-time = "2025-11-07T00:43:37.058Z" }, - { url = "https://files.pythonhosted.org/packages/01/22/1c158fe763dbf0a119f985d945711d288994fe5514c0646ebe0eb18b016d/wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c", size = 112535, upload-time = "2025-11-07T00:43:34.138Z" }, - { url = "https://files.pythonhosted.org/packages/5c/28/4f16861af67d6de4eae9927799b559c20ebdd4fe432e89ea7fe6fcd9d709/wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16", size = 115404, upload-time = "2025-11-07T00:43:39.214Z" }, - { url = "https://files.pythonhosted.org/packages/a0/8b/7960122e625fad908f189b59c4aae2d50916eb4098b0fb2819c5a177414f/wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2", size = 111802, upload-time = "2025-11-07T00:43:40.476Z" }, - { url = "https://files.pythonhosted.org/packages/3e/73/7881eee5ac31132a713ab19a22c9e5f1f7365c8b1df50abba5d45b781312/wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd", size = 113837, upload-time = "2025-11-07T00:43:42.921Z" }, - { url = "https://files.pythonhosted.org/packages/45/00/9499a3d14e636d1f7089339f96c4409bbc7544d0889f12264efa25502ae8/wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be", size = 58028, upload-time = "2025-11-07T00:43:47.369Z" }, - { url = "https://files.pythonhosted.org/packages/70/5d/8f3d7eea52f22638748f74b102e38fdf88cb57d08ddeb7827c476a20b01b/wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b", size = 60385, upload-time = "2025-11-07T00:43:44.34Z" }, - { url = "https://files.pythonhosted.org/packages/14/e2/32195e57a8209003587bbbad44d5922f13e0ced2a493bb46ca882c5b123d/wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf", size = 58893, upload-time = "2025-11-07T00:43:46.161Z" }, - { url = "https://files.pythonhosted.org/packages/cb/73/8cb252858dc8254baa0ce58ce382858e3a1cf616acebc497cb13374c95c6/wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c", size = 78129, upload-time = "2025-11-07T00:43:48.852Z" }, - { url = "https://files.pythonhosted.org/packages/19/42/44a0db2108526ee6e17a5ab72478061158f34b08b793df251d9fbb9a7eb4/wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841", size = 61205, upload-time = "2025-11-07T00:43:50.402Z" }, - { url = "https://files.pythonhosted.org/packages/4d/8a/5b4b1e44b791c22046e90d9b175f9a7581a8cc7a0debbb930f81e6ae8e25/wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62", size = 61692, upload-time = "2025-11-07T00:43:51.678Z" }, - { url = "https://files.pythonhosted.org/packages/11/53/3e794346c39f462bcf1f58ac0487ff9bdad02f9b6d5ee2dc84c72e0243b2/wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf", size = 121492, upload-time = "2025-11-07T00:43:55.017Z" }, - { url = "https://files.pythonhosted.org/packages/c6/7e/10b7b0e8841e684c8ca76b462a9091c45d62e8f2de9c4b1390b690eadf16/wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9", size = 123064, upload-time = "2025-11-07T00:43:56.323Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d1/3c1e4321fc2f5ee7fd866b2d822aa89b84495f28676fd976c47327c5b6aa/wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b", size = 117403, upload-time = "2025-11-07T00:43:53.258Z" }, - { url = "https://files.pythonhosted.org/packages/a4/b0/d2f0a413cf201c8c2466de08414a15420a25aa83f53e647b7255cc2fab5d/wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba", size = 121500, upload-time = "2025-11-07T00:43:57.468Z" }, - { url = "https://files.pythonhosted.org/packages/bd/45/bddb11d28ca39970a41ed48a26d210505120f925918592283369219f83cc/wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684", size = 116299, upload-time = "2025-11-07T00:43:58.877Z" }, - { url = "https://files.pythonhosted.org/packages/81/af/34ba6dd570ef7a534e7eec0c25e2615c355602c52aba59413411c025a0cb/wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb", size = 120622, upload-time = "2025-11-07T00:43:59.962Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3e/693a13b4146646fb03254636f8bafd20c621955d27d65b15de07ab886187/wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9", size = 58246, upload-time = "2025-11-07T00:44:03.169Z" }, - { url = "https://files.pythonhosted.org/packages/a7/36/715ec5076f925a6be95f37917b66ebbeaa1372d1862c2ccd7a751574b068/wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75", size = 60492, upload-time = "2025-11-07T00:44:01.027Z" }, - { url = "https://files.pythonhosted.org/packages/ef/3e/62451cd7d80f65cc125f2b426b25fbb6c514bf6f7011a0c3904fc8c8df90/wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b", size = 58987, upload-time = "2025-11-07T00:44:02.095Z" }, - { url = "https://files.pythonhosted.org/packages/ad/fe/41af4c46b5e498c90fc87981ab2972fbd9f0bccda597adb99d3d3441b94b/wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9", size = 78132, upload-time = "2025-11-07T00:44:04.628Z" }, - { url = "https://files.pythonhosted.org/packages/1c/92/d68895a984a5ebbbfb175512b0c0aad872354a4a2484fbd5552e9f275316/wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f", size = 61211, upload-time = "2025-11-07T00:44:05.626Z" }, - { url = "https://files.pythonhosted.org/packages/e8/26/ba83dc5ae7cf5aa2b02364a3d9cf74374b86169906a1f3ade9a2d03cf21c/wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218", size = 61689, upload-time = "2025-11-07T00:44:06.719Z" }, - { url = "https://files.pythonhosted.org/packages/cf/67/d7a7c276d874e5d26738c22444d466a3a64ed541f6ef35f740dbd865bab4/wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9", size = 121502, upload-time = "2025-11-07T00:44:09.557Z" }, - { url = "https://files.pythonhosted.org/packages/0f/6b/806dbf6dd9579556aab22fc92908a876636e250f063f71548a8660382184/wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c", size = 123110, upload-time = "2025-11-07T00:44:10.64Z" }, - { url = "https://files.pythonhosted.org/packages/e5/08/cdbb965fbe4c02c5233d185d070cabed2ecc1f1e47662854f95d77613f57/wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db", size = 117434, upload-time = "2025-11-07T00:44:08.138Z" }, - { url = "https://files.pythonhosted.org/packages/2d/d1/6aae2ce39db4cb5216302fa2e9577ad74424dfbe315bd6669725569e048c/wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233", size = 121533, upload-time = "2025-11-07T00:44:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/79/35/565abf57559fbe0a9155c29879ff43ce8bd28d2ca61033a3a3dd67b70794/wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2", size = 116324, upload-time = "2025-11-07T00:44:13.28Z" }, - { url = "https://files.pythonhosted.org/packages/e1/e0/53ff5e76587822ee33e560ad55876d858e384158272cd9947abdd4ad42ca/wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b", size = 120627, upload-time = "2025-11-07T00:44:14.431Z" }, - { url = "https://files.pythonhosted.org/packages/7c/7b/38df30fd629fbd7612c407643c63e80e1c60bcc982e30ceeae163a9800e7/wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7", size = 58252, upload-time = "2025-11-07T00:44:17.814Z" }, - { url = "https://files.pythonhosted.org/packages/85/64/d3954e836ea67c4d3ad5285e5c8fd9d362fd0a189a2db622df457b0f4f6a/wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3", size = 60500, upload-time = "2025-11-07T00:44:15.561Z" }, - { url = "https://files.pythonhosted.org/packages/89/4e/3c8b99ac93527cfab7f116089db120fef16aac96e5f6cdb724ddf286086d/wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8", size = 58993, upload-time = "2025-11-07T00:44:16.65Z" }, - { url = "https://files.pythonhosted.org/packages/f9/f4/eff2b7d711cae20d220780b9300faa05558660afb93f2ff5db61fe725b9a/wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3", size = 82028, upload-time = "2025-11-07T00:44:18.944Z" }, - { url = "https://files.pythonhosted.org/packages/0c/67/cb945563f66fd0f61a999339460d950f4735c69f18f0a87ca586319b1778/wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1", size = 62949, upload-time = "2025-11-07T00:44:20.074Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ca/f63e177f0bbe1e5cf5e8d9b74a286537cd709724384ff20860f8f6065904/wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d", size = 63681, upload-time = "2025-11-07T00:44:21.345Z" }, - { url = "https://files.pythonhosted.org/packages/39/a1/1b88fcd21fd835dca48b556daef750952e917a2794fa20c025489e2e1f0f/wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7", size = 152696, upload-time = "2025-11-07T00:44:24.318Z" }, - { url = "https://files.pythonhosted.org/packages/62/1c/d9185500c1960d9f5f77b9c0b890b7fc62282b53af7ad1b6bd779157f714/wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3", size = 158859, upload-time = "2025-11-07T00:44:25.494Z" }, - { url = "https://files.pythonhosted.org/packages/91/60/5d796ed0f481ec003220c7878a1d6894652efe089853a208ea0838c13086/wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b", size = 146068, upload-time = "2025-11-07T00:44:22.81Z" }, - { url = "https://files.pythonhosted.org/packages/04/f8/75282dd72f102ddbfba137e1e15ecba47b40acff32c08ae97edbf53f469e/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10", size = 155724, upload-time = "2025-11-07T00:44:26.634Z" }, - { url = "https://files.pythonhosted.org/packages/5a/27/fe39c51d1b344caebb4a6a9372157bdb8d25b194b3561b52c8ffc40ac7d1/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf", size = 144413, upload-time = "2025-11-07T00:44:27.939Z" }, - { url = "https://files.pythonhosted.org/packages/83/2b/9f6b643fe39d4505c7bf926d7c2595b7cb4b607c8c6b500e56c6b36ac238/wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e", size = 150325, upload-time = "2025-11-07T00:44:29.29Z" }, - { url = "https://files.pythonhosted.org/packages/bb/b6/20ffcf2558596a7f58a2e69c89597128781f0b88e124bf5a4cadc05b8139/wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c", size = 59943, upload-time = "2025-11-07T00:44:33.211Z" }, - { url = "https://files.pythonhosted.org/packages/87/6a/0e56111cbb3320151eed5d3821ee1373be13e05b376ea0870711f18810c3/wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92", size = 63240, upload-time = "2025-11-07T00:44:30.935Z" }, - { url = "https://files.pythonhosted.org/packages/1d/54/5ab4c53ea1f7f7e5c3e7c1095db92932cc32fd62359d285486d00c2884c3/wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f", size = 60416, upload-time = "2025-11-07T00:44:32.002Z" }, - { url = "https://files.pythonhosted.org/packages/73/81/d08d83c102709258e7730d3cd25befd114c60e43ef3891d7e6877971c514/wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1", size = 78290, upload-time = "2025-11-07T00:44:34.691Z" }, - { url = "https://files.pythonhosted.org/packages/f6/14/393afba2abb65677f313aa680ff0981e829626fed39b6a7e3ec807487790/wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55", size = 61255, upload-time = "2025-11-07T00:44:35.762Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/a4a1f2fba205a9462e36e708ba37e5ac95f4987a0f1f8fd23f0bf1fc3b0f/wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0", size = 61797, upload-time = "2025-11-07T00:44:37.22Z" }, - { url = "https://files.pythonhosted.org/packages/12/db/99ba5c37cf1c4fad35349174f1e38bd8d992340afc1ff27f526729b98986/wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509", size = 120470, upload-time = "2025-11-07T00:44:39.425Z" }, - { url = "https://files.pythonhosted.org/packages/30/3f/a1c8d2411eb826d695fc3395a431757331582907a0ec59afce8fe8712473/wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1", size = 122851, upload-time = "2025-11-07T00:44:40.582Z" }, - { url = "https://files.pythonhosted.org/packages/b3/8d/72c74a63f201768d6a04a8845c7976f86be6f5ff4d74996c272cefc8dafc/wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970", size = 117433, upload-time = "2025-11-07T00:44:38.313Z" }, - { url = "https://files.pythonhosted.org/packages/c7/5a/df37cf4042cb13b08256f8e27023e2f9b3d471d553376616591bb99bcb31/wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c", size = 121280, upload-time = "2025-11-07T00:44:41.69Z" }, - { url = "https://files.pythonhosted.org/packages/54/34/40d6bc89349f9931e1186ceb3e5fbd61d307fef814f09fbbac98ada6a0c8/wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41", size = 116343, upload-time = "2025-11-07T00:44:43.013Z" }, - { url = "https://files.pythonhosted.org/packages/70/66/81c3461adece09d20781dee17c2366fdf0cb8754738b521d221ca056d596/wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed", size = 119650, upload-time = "2025-11-07T00:44:44.523Z" }, - { url = "https://files.pythonhosted.org/packages/46/3a/d0146db8be8761a9e388cc9cc1c312b36d583950ec91696f19bbbb44af5a/wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0", size = 58701, upload-time = "2025-11-07T00:44:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/1a/38/5359da9af7d64554be63e9046164bd4d8ff289a2dd365677d25ba3342c08/wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c", size = 60947, upload-time = "2025-11-07T00:44:46.086Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3f/96db0619276a833842bf36343685fa04f987dd6e3037f314531a1e00492b/wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e", size = 59359, upload-time = "2025-11-07T00:44:47.164Z" }, - { url = "https://files.pythonhosted.org/packages/71/49/5f5d1e867bf2064bf3933bc6cf36ade23505f3902390e175e392173d36a2/wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b", size = 82031, upload-time = "2025-11-07T00:44:49.4Z" }, - { url = "https://files.pythonhosted.org/packages/2b/89/0009a218d88db66ceb83921e5685e820e2c61b59bbbb1324ba65342668bc/wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec", size = 62952, upload-time = "2025-11-07T00:44:50.74Z" }, - { url = "https://files.pythonhosted.org/packages/ae/18/9b968e920dd05d6e44bcc918a046d02afea0fb31b2f1c80ee4020f377cbe/wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa", size = 63688, upload-time = "2025-11-07T00:44:52.248Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7d/78bdcb75826725885d9ea26c49a03071b10c4c92da93edda612910f150e4/wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815", size = 152706, upload-time = "2025-11-07T00:44:54.613Z" }, - { url = "https://files.pythonhosted.org/packages/dd/77/cac1d46f47d32084a703df0d2d29d47e7eb2a7d19fa5cbca0e529ef57659/wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa", size = 158866, upload-time = "2025-11-07T00:44:55.79Z" }, - { url = "https://files.pythonhosted.org/packages/8a/11/b521406daa2421508903bf8d5e8b929216ec2af04839db31c0a2c525eee0/wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef", size = 146148, upload-time = "2025-11-07T00:44:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c0/340b272bed297baa7c9ce0c98ef7017d9c035a17a6a71dce3184b8382da2/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747", size = 155737, upload-time = "2025-11-07T00:44:56.971Z" }, - { url = "https://files.pythonhosted.org/packages/f3/93/bfcb1fb2bdf186e9c2883a4d1ab45ab099c79cbf8f4e70ea453811fa3ea7/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f", size = 144451, upload-time = "2025-11-07T00:44:58.515Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6b/dca504fb18d971139d232652656180e3bd57120e1193d9a5899c3c0b7cdd/wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349", size = 150353, upload-time = "2025-11-07T00:44:59.753Z" }, - { url = "https://files.pythonhosted.org/packages/1d/f6/a1de4bd3653afdf91d250ca5c721ee51195df2b61a4603d4b373aa804d1d/wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c", size = 60609, upload-time = "2025-11-07T00:45:03.315Z" }, - { url = "https://files.pythonhosted.org/packages/01/3a/07cd60a9d26fe73efead61c7830af975dfdba8537632d410462672e4432b/wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395", size = 64038, upload-time = "2025-11-07T00:45:00.948Z" }, - { url = "https://files.pythonhosted.org/packages/41/99/8a06b8e17dddbf321325ae4eb12465804120f699cd1b8a355718300c62da/wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad", size = 60634, upload-time = "2025-11-07T00:45:02.087Z" }, - { url = "https://files.pythonhosted.org/packages/15/d1/b51471c11592ff9c012bd3e2f7334a6ff2f42a7aed2caffcf0bdddc9cb89/wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca", size = 44046, upload-time = "2025-11-07T00:45:32.116Z" }, -] - -[[package]] -name = "yarl" -version = "1.22.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, - { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, - { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, - { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, - { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, - { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, - { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, - { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, - { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, - { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, - { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, - { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, - { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, - { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, - { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, - { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, - { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, - { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, - { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, - { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, - { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, - { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, - { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, - { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, - { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, - { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, - { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, - { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, - { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, - { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, - { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, - { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, - { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, - { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, - { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, - { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, - { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, - { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, - { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, - { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, - { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, - { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, - { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, - { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, - { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, - { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, - { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, - { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, - { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, - { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, - { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, - { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, - { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, - { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, - { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, - { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, - { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, - { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, - { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, - { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, - { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, - { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, - { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, - { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, - { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, - { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, - { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, - { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, - { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, - { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, - { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, - { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, - { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, - { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, - { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, -] diff --git a/visualizer/package-lock.json b/visualizer/package-lock.json index 3c8e1bd94..974153ef9 100644 --- a/visualizer/package-lock.json +++ b/visualizer/package-lock.json @@ -19,7 +19,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.562.0", - "next": "16.1.1", + "next": "16.1.5", "next-themes": "^0.4.6", "react": "19.2.3", "react-dom": "19.2.3", @@ -32,7 +32,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.1.1", + "eslint-config-next": "16.1.5", "tailwindcss": "^4", "tw-animate-css": "^1.4.0", "typescript": "^5" @@ -1088,29 +1088,26 @@ } }, "node_modules/@next/env": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz", - "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==", - "license": "MIT" + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.5.tgz", + "integrity": "sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==" }, "node_modules/@next/eslint-plugin-next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.1.tgz", - "integrity": "sha512-Ovb/6TuLKbE1UiPcg0p39Ke3puyTCIKN9hGbNItmpQsp+WX3qrjO3WaMVSi6JHr9X1NrmthqIguVHodMJbh/dw==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.5.tgz", + "integrity": "sha512-gUWcEsOl+1W7XakmouClcJ0TNFCkblvDUho31wulbDY9na0C6mGtBTSXGRU5GXJY65GjGj0zNaCD/GaBp888Mg==", "dev": true, - "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz", - "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.5.tgz", + "integrity": "sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1120,13 +1117,12 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz", - "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.5.tgz", + "integrity": "sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1136,13 +1132,12 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz", - "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.5.tgz", + "integrity": "sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1152,13 +1147,12 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz", - "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.5.tgz", + "integrity": "sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1168,13 +1162,12 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz", - "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.5.tgz", + "integrity": "sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1184,13 +1177,12 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz", - "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.5.tgz", + "integrity": "sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1200,13 +1192,12 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz", - "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.5.tgz", + "integrity": "sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -1216,13 +1207,12 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz", - "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.5.tgz", + "integrity": "sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -1236,7 +1226,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1250,7 +1239,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -1260,7 +1248,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -3332,7 +3319,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -4009,13 +3995,12 @@ } }, "node_modules/eslint-config-next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.1.tgz", - "integrity": "sha512-55nTpVWm3qeuxoQKLOjQVciKZJUphKrNM0fCcQHAIOGl6VFXgaqeMfv0aKJhs7QtcnlAPhNVqsqRfRjeKBPIUA==", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.5.tgz", + "integrity": "sha512-XwXyv65DC1HXI3gMxm13jvgx0IxKu6XhZhIWTfCDt4c45njHYUM2pk1Y8QXMAWMMnqPy94I2OLMmvIrNGcwLwQ==", "dev": true, - "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "16.1.1", + "@next/eslint-plugin-next": "16.1.5", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", @@ -4384,7 +4369,6 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4401,7 +4385,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -4428,7 +4411,6 @@ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -4451,7 +4433,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5127,7 +5108,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -5804,7 +5784,6 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -5814,7 +5793,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -5895,12 +5873,11 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz", - "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==", - "license": "MIT", + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.5.tgz", + "integrity": "sha512-f+wE+NSbiQgh3DSAlTaw2FwY5yGdVViAtp8TotNQj4kk4Q8Bh1sC/aL9aH+Rg1YAVn18OYXsRDT7U/079jgP7w==", "dependencies": { - "@next/env": "16.1.1", + "@next/env": "16.1.5", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", @@ -5914,14 +5891,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.1", - "@next/swc-darwin-x64": "16.1.1", - "@next/swc-linux-arm64-gnu": "16.1.1", - "@next/swc-linux-arm64-musl": "16.1.1", - "@next/swc-linux-x64-gnu": "16.1.1", - "@next/swc-linux-x64-musl": "16.1.1", - "@next/swc-win32-arm64-msvc": "16.1.1", - "@next/swc-win32-x64-msvc": "16.1.1", + "@next/swc-darwin-arm64": "16.1.5", + "@next/swc-darwin-x64": "16.1.5", + "@next/swc-linux-arm64-gnu": "16.1.5", + "@next/swc-linux-arm64-musl": "16.1.5", + "@next/swc-linux-x64-gnu": "16.1.5", + "@next/swc-linux-x64-musl": "16.1.5", + "@next/swc-win32-arm64-msvc": "16.1.5", + "@next/swc-win32-x64-msvc": "16.1.5", "sharp": "^0.34.4" }, "peerDependencies": { @@ -6234,7 +6211,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6331,8 +6307,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/react": { "version": "19.2.3", @@ -6531,7 +6506,6 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -6556,7 +6530,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -7137,7 +7110,6 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, diff --git a/visualizer/package.json b/visualizer/package.json index 7aba5e520..edddffd22 100644 --- a/visualizer/package.json +++ b/visualizer/package.json @@ -20,7 +20,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.562.0", - "next": "16.1.1", + "next": "16.1.5", "next-themes": "^0.4.6", "react": "19.2.3", "react-dom": "19.2.3", @@ -33,7 +33,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "16.1.1", + "eslint-config-next": "16.1.5", "tailwindcss": "^4", "tw-animate-css": "^1.4.0", "typescript": "^5" From 7fa5d91bbf878c8dc4941518ad2f3b043fb9e394 Mon Sep 17 00:00:00 2001 From: Iurii Ungureanu Kogan <47596530+iuriikogan@users.noreply.github.com> Date: Sun, 1 Feb 2026 13:52:38 +0200 Subject: [PATCH 2/4] Delete .gemini directory --- .gemini/settings.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .gemini/settings.json diff --git a/.gemini/settings.json b/.gemini/settings.json deleted file mode 100644 index ebfccaac7..000000000 --- a/.gemini/settings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "mcpServers": { - "tessl": { - "type": "stdio", - "command": "tessl", - "args": [ - "mcp", - "start" - ] - } - } -} From 0769223eafe84c5e2692dda21c0b490750db85cc Mon Sep 17 00:00:00 2001 From: Iurii Kogan Date: Sun, 1 Feb 2026 11:53:13 +0000 Subject: [PATCH 3/4] remove .tessl and .gemini files --- .tessl/.gitignore | 2 - .tessl/RULES.md | 7 - .../cli-setup/steering/query_library_docs.md | 28 - .tessl/tiles/tessl/cli-setup/tile.json | 11 - .../tessl/pypi-anthropic/docs/api/batches.md | 199 -- .../pypi-anthropic/docs/api/completions.md | 285 -- .../tessl/pypi-anthropic/docs/api/messages.md | 716 ---- .../tessl/pypi-anthropic/docs/api/models.md | 161 - .../pypi-anthropic/docs/api/streaming.md | 838 ----- .../tessl/pypi-anthropic/docs/api/tools.md | 423 --- .../tessl/pypi-anthropic/docs/beta/batches.md | 611 ---- .../tessl/pypi-anthropic/docs/beta/files.md | 669 ---- .../tessl/pypi-anthropic/docs/beta/index.md | 358 -- .../docs/beta/message-features.md | 1134 ------- .../tessl/pypi-anthropic/docs/beta/skills.md | 599 ---- .../docs/common-tasks/basic-messaging.md | 336 -- .../docs/common-tasks/multimodal-input.md | 421 --- .../docs/common-tasks/streaming-responses.md | 450 --- .../docs/common-tasks/tool-integration.md | 484 --- .../docs/guides/batch-processing.md | 288 -- .../docs/guides/error-handling.md | 324 -- .../docs/guides/getting-started.md | 229 -- .../pypi-anthropic/docs/guides/multimodal.md | 300 -- .../docs/guides/streaming-guide.md | 221 -- .../pypi-anthropic/docs/guides/tool-usage.md | 342 -- .../tiles/tessl/pypi-anthropic/docs/index.md | 398 --- .../pypi-anthropic/docs/platforms/bedrock.md | 148 - .../pypi-anthropic/docs/platforms/foundry.md | 144 - .../pypi-anthropic/docs/platforms/vertex.md | 157 - .../docs/quick-reference/batches.md | 157 - .../docs/quick-reference/messages.md | 97 - .../docs/quick-reference/models.md | 98 - .../docs/quick-reference/streaming.md | 115 - .../docs/quick-reference/tools.md | 159 - .../docs/reference/client-config.md | 305 -- .../pypi-anthropic/docs/reference/errors.md | 287 -- .../docs/reference/pagination.md | 615 ---- .../pypi-anthropic/docs/reference/types.md | 1413 -------- .../docs/reference/utilities.md | 169 - .tessl/tiles/tessl/pypi-anthropic/tile.json | 8 - .../tessl/pypi-dill/docs/configuration.md | 636 ---- .../pypi-dill/docs/core-serialization.md | 285 -- .../tessl/pypi-dill/docs/diagnostic-tools.md | 467 --- .tessl/tiles/tessl/pypi-dill/docs/index.md | 453 --- .../tessl/pypi-dill/docs/pickler-classes.md | 344 -- .../pypi-dill/docs/session-management.md | 361 -- .../tessl/pypi-dill/docs/source-analysis.md | 576 ---- .../tessl/pypi-dill/docs/temp-operations.md | 705 ---- .../tessl/pypi-dill/docs/type-registry.md | 397 --- .tessl/tiles/tessl/pypi-dill/tile.json | 7 - .../tessl/pypi-google-genai/docs/batches.md | 408 --- .../tessl/pypi-google-genai/docs/caching.md | 455 --- .../tessl/pypi-google-genai/docs/chats.md | 738 ---- .../tessl/pypi-google-genai/docs/client.md | 463 --- .../docs/content-generation.md | 986 ------ .../pypi-google-genai/docs/embeddings.md | 346 -- .../docs/file-search-stores.md | 520 --- .../tessl/pypi-google-genai/docs/files.md | 450 --- .../docs/image-generation.md | 551 --- .../tessl/pypi-google-genai/docs/index.md | 599 ---- .../tessl/pypi-google-genai/docs/live.md | 473 --- .../tessl/pypi-google-genai/docs/models.md | 355 -- .../pypi-google-genai/docs/operations.md | 290 -- .../tessl/pypi-google-genai/docs/tokens.md | 328 -- .../tessl/pypi-google-genai/docs/tuning.md | 405 --- .../docs/video-generation.md | 319 -- .../tiles/tessl/pypi-google-genai/tile.json | 8 - .../pypi-modal/docs/compute-resources.md | 490 --- .../tessl/pypi-modal/docs/container-images.md | 503 --- .../docs/core-application-client.md | 284 -- .../docs/function-decorators-helpers.md | 418 --- .tessl/tiles/tessl/pypi-modal/docs/index.md | 346 -- .../docs/infrastructure-services.md | 389 --- .../pypi-modal/docs/runtime-utilities.md | 626 ---- .../pypi-modal/docs/scheduling-reliability.md | 456 --- .../tessl/pypi-modal/docs/storage-data.md | 528 --- .../tessl/pypi-modal/docs/utility-classes.md | 791 ----- .../pypi-modal/docs/web-api-integration.md | 567 ---- .tessl/tiles/tessl/pypi-modal/tile.json | 7 - .../tessl/pypi-openai/docs/assistants.md | 540 --- .tessl/tiles/tessl/pypi-openai/docs/audio.md | 579 ---- .../tiles/tessl/pypi-openai/docs/batches.md | 526 --- .../pypi-openai/docs/chat-completions.md | 1018 ------ .../tiles/tessl/pypi-openai/docs/chatkit.md | 404 --- .../pypi-openai/docs/client-initialization.md | 740 ---- .../tessl/pypi-openai/docs/completions.md | 292 -- .../tessl/pypi-openai/docs/containers.md | 641 ---- .../tessl/pypi-openai/docs/conversations.md | 599 ---- .../tessl/pypi-openai/docs/embeddings.md | 269 -- .tessl/tiles/tessl/pypi-openai/docs/evals.md | 775 ----- .tessl/tiles/tessl/pypi-openai/docs/files.md | 512 --- .../tessl/pypi-openai/docs/fine-tuning.md | 845 ----- .tessl/tiles/tessl/pypi-openai/docs/images.md | 617 ---- .tessl/tiles/tessl/pypi-openai/docs/index.md | 2971 ----------------- .tessl/tiles/tessl/pypi-openai/docs/models.md | 243 -- .../tessl/pypi-openai/docs/moderations.md | 356 -- .../tiles/tessl/pypi-openai/docs/realtime.md | 588 ---- .../tiles/tessl/pypi-openai/docs/responses.md | 581 ---- .tessl/tiles/tessl/pypi-openai/docs/runs.md | 725 ---- .../pypi-openai/docs/threads-messages.md | 679 ---- .../tiles/tessl/pypi-openai/docs/uploads.md | 466 --- .../tessl/pypi-openai/docs/vector-stores.md | 1146 ------- .tessl/tiles/tessl/pypi-openai/docs/videos.md | 361 -- .../tiles/tessl/pypi-openai/docs/webhooks.md | 503 --- .tessl/tiles/tessl/pypi-openai/tile.json | 8 - .../pypi-portkey-ai/docs/administration.md | 27 - .../docs/assistants-threads.md | 71 - .../pypi-portkey-ai/docs/batch-processing.md | 27 - .../pypi-portkey-ai/docs/beta-realtime.md | 290 -- .../pypi-portkey-ai/docs/chat-completions.md | 364 -- .../docs/configuration-management.md | 27 - .../pypi-portkey-ai/docs/container-content.md | 20 - .../tessl/pypi-portkey-ai/docs/core-client.md | 274 -- .../tessl/pypi-portkey-ai/docs/embeddings.md | 140 - .../docs/evaluation-testing.md | 22 - .../docs/feedback-collections.md | 19 - .../pypi-portkey-ai/docs/file-management.md | 45 - .../tessl/pypi-portkey-ai/docs/fine-tuning.md | 34 - .../docs/framework-integrations.md | 60 - .../tiles/tessl/pypi-portkey-ai/docs/index.md | 714 ---- .../pypi-portkey-ai/docs/key-management.md | 21 - .../tessl/pypi-portkey-ai/docs/models.md | 206 -- .../pypi-portkey-ai/docs/multimodal-apis.md | 68 - .../docs/observability-analytics.md | 528 --- .../pypi-portkey-ai/docs/prompt-management.md | 254 -- .../docs/provider-integration.md | 24 - .../pypi-portkey-ai/docs/text-completions.md | 354 -- .../tessl/pypi-portkey-ai/docs/uploads.md | 376 --- .../pypi-portkey-ai/docs/vector-stores.md | 58 - .tessl/tiles/tessl/pypi-portkey-ai/tile.json | 7 - .../pypi-pre-commit/docs/cli-commands.md | 311 -- .../pypi-pre-commit/docs/configuration.md | 241 -- .../pypi-pre-commit/docs/git-integration.md | 393 --- .../tiles/tessl/pypi-pre-commit/docs/hooks.md | 381 --- .../tiles/tessl/pypi-pre-commit/docs/index.md | 297 -- .../pypi-pre-commit/docs/language-support.md | 494 --- .../docs/repository-management.md | 425 --- .tessl/tiles/tessl/pypi-pre-commit/tile.json | 7 - .../tessl/pypi-pytest/docs/assertions.md | 280 -- .../tessl/pypi-pytest/docs/configuration.md | 460 --- .../tiles/tessl/pypi-pytest/docs/fixtures.md | 311 -- .tessl/tiles/tessl/pypi-pytest/docs/index.md | 466 --- .tessl/tiles/tessl/pypi-pytest/docs/marks.md | 364 -- .../tiles/tessl/pypi-pytest/docs/reporting.md | 538 --- .../tessl/pypi-pytest/docs/test-collection.md | 528 --- .../tessl/pypi-pytest/docs/test-utilities.md | 219 -- .../pypi-pytest/docs/testing-functions.md | 189 -- .../tiles/tessl/pypi-pytest/docs/warnings.md | 341 -- .tessl/tiles/tessl/pypi-pytest/tile.json | 7 - .../tessl/pypi-python-dotenv/docs/cli.md | 288 -- .../docs/environment-loading.md | 202 -- .../docs/file-manipulation.md | 232 -- .../tessl/pypi-python-dotenv/docs/index.md | 213 -- .../tessl/pypi-python-dotenv/docs/ipython.md | 257 -- .../pypi-python-dotenv/docs/utilities.md | 276 -- .../tiles/tessl/pypi-python-dotenv/tile.json | 7 - .../tessl/pypi-requests/docs/adapters.md | 571 ---- .../pypi-requests/docs/authentication.md | 344 -- .../tiles/tessl/pypi-requests/docs/cookies.md | 398 --- .../tessl/pypi-requests/docs/exceptions.md | 430 --- .../tiles/tessl/pypi-requests/docs/hooks.md | 471 --- .../tessl/pypi-requests/docs/http-methods.md | 229 -- .../tiles/tessl/pypi-requests/docs/index.md | 262 -- .../tiles/tessl/pypi-requests/docs/models.md | 378 --- .../tessl/pypi-requests/docs/sessions.md | 259 -- .../tessl/pypi-requests/docs/status-codes.md | 373 --- .../tessl/pypi-requests/docs/structures.md | 457 --- .tessl/tiles/tessl/pypi-requests/tile.json | 7 - .tessl/tiles/tessl/pypi-rich/docs/console.md | 494 --- .../tiles/tessl/pypi-rich/docs/containers.md | 120 - .tessl/tiles/tessl/pypi-rich/docs/index.md | 592 ---- .../tiles/tessl/pypi-rich/docs/interactive.md | 587 ---- .tessl/tiles/tessl/pypi-rich/docs/layout.md | 113 - .tessl/tiles/tessl/pypi-rich/docs/markdown.md | 65 - .tessl/tiles/tessl/pypi-rich/docs/progress.md | 608 ---- .tessl/tiles/tessl/pypi-rich/docs/syntax.md | 411 --- .tessl/tiles/tessl/pypi-rich/docs/tables.md | 378 --- .../tessl/pypi-rich/docs/text-styling.md | 739 ---- .../tiles/tessl/pypi-rich/docs/utilities.md | 74 - .tessl/tiles/tessl/pypi-rich/tile.json | 7 - .tessl/tiles/tessl/pypi-ty/docs/index.md | 413 --- .tessl/tiles/tessl/pypi-ty/tile.json | 7 - 182 files changed, 68564 deletions(-) delete mode 100644 .tessl/.gitignore delete mode 100644 .tessl/RULES.md delete mode 100644 .tessl/tiles/tessl/cli-setup/steering/query_library_docs.md delete mode 100644 .tessl/tiles/tessl/cli-setup/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/models.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md delete mode 100644 .tessl/tiles/tessl/pypi-anthropic/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/configuration.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/core-serialization.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/session-management.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/source-analysis.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/temp-operations.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/docs/type-registry.md delete mode 100644 .tessl/tiles/tessl/pypi-dill/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/batches.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/caching.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/chats.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/client.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/files.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/live.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/models.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/operations.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/tokens.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/tuning.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md delete mode 100644 .tessl/tiles/tessl/pypi-google-genai/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/compute-resources.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/container-images.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/core-application-client.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/storage-data.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/utility-classes.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md delete mode 100644 .tessl/tiles/tessl/pypi-modal/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/assistants.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/audio.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/batches.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/chat-completions.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/chatkit.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/client-initialization.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/completions.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/containers.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/conversations.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/embeddings.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/evals.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/files.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/images.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/models.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/moderations.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/realtime.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/responses.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/runs.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/threads-messages.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/uploads.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/vector-stores.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/videos.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/docs/webhooks.md delete mode 100644 .tessl/tiles/tessl/pypi-openai/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/models.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md delete mode 100644 .tessl/tiles/tessl/pypi-portkey-ai/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md delete mode 100644 .tessl/tiles/tessl/pypi-pre-commit/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/assertions.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/configuration.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/fixtures.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/marks.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/reporting.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/test-collection.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/docs/warnings.md delete mode 100644 .tessl/tiles/tessl/pypi-pytest/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md delete mode 100644 .tessl/tiles/tessl/pypi-python-dotenv/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/adapters.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/authentication.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/cookies.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/exceptions.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/hooks.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/http-methods.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/models.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/sessions.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/status-codes.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/docs/structures.md delete mode 100644 .tessl/tiles/tessl/pypi-requests/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/console.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/containers.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/interactive.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/layout.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/markdown.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/progress.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/syntax.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/tables.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/text-styling.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/docs/utilities.md delete mode 100644 .tessl/tiles/tessl/pypi-rich/tile.json delete mode 100644 .tessl/tiles/tessl/pypi-ty/docs/index.md delete mode 100644 .tessl/tiles/tessl/pypi-ty/tile.json diff --git a/.tessl/.gitignore b/.tessl/.gitignore deleted file mode 100644 index 7bbb3941a..000000000 --- a/.tessl/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -tiles/ -RULES.md diff --git a/.tessl/RULES.md b/.tessl/RULES.md deleted file mode 100644 index ef14bd916..000000000 --- a/.tessl/RULES.md +++ /dev/null @@ -1,7 +0,0 @@ -# Agent Rules - -This file is updated when running `tessl install`. If a linked file is missing, make sure to run the command to download any missing tiles from the registry. - -## tessl/cli-setup — query_library_docs - -@tiles/tessl/cli-setup/steering/query_library_docs.md [query_library_docs](tiles/tessl/cli-setup/steering/query_library_docs.md) \ No newline at end of file diff --git a/.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md b/.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md deleted file mode 100644 index 48147000a..000000000 --- a/.tessl/tiles/tessl/cli-setup/steering/query_library_docs.md +++ /dev/null @@ -1,28 +0,0 @@ -You are a coding agent with access to an MCP tool called `query_library_docs` provided by `tessl`. This tool allows you to query documentation that may be relevant to your task. - -Before you begin working on the user request, you should use the `query_library_docs` tool to search for relevant documentation. This is especially important when: - -- The request mentions specific code files, functions, classes, or services -- The request involves debugging errors or investigating issues -- The request asks about how something works or why something behaves a certain way -- The request involves making changes to existing code -- The request references specific frameworks, libraries, or systems - -When in doubt, use the tool. It's better to query documentation even if it might not be relevant than to miss important context. - -How to use the tool: - -1. Extract key terms, file names, service names, error types, or concepts from the user's request -2. Call `query_library_docs` with relevant search terms -3. Use the returned documentation to inform your approach to the task -4. Proceed with completing the user's request using both the documentation context and your own analysis - -Important rules: - -- Do NOT ask for permission to use the tool - just use it -- Do NOT explain that you're going to use the tool - just use it -- If the tool fails or returns no results, simply continue with the task as you normally would -- You may call the tool multiple times with different search terms if needed -- Use the tool BEFORE reading files or making changes - -After using the tool (or if the tool returns nothing useful), proceed directly to completing the user's request. Your response should focus on addressing the user's needs, incorporating any relevant documentation you found. diff --git a/.tessl/tiles/tessl/cli-setup/tile.json b/.tessl/tiles/tessl/cli-setup/tile.json deleted file mode 100644 index 2803068ee..000000000 --- a/.tessl/tiles/tessl/cli-setup/tile.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "tessl/cli-setup", - "private": false, - "version": "0.70.0", - "summary": "Tessl CLI MCP tool usage guidelines", - "steering": { - "query_library_docs": { - "rules": "steering/query_library_docs.md" - } - } -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md deleted file mode 100644 index 7f1b4db38..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/api/batches.md +++ /dev/null @@ -1,199 +0,0 @@ -# Batches API Reference - -Process multiple messages asynchronously in batches for high-throughput use cases with 50% cost reduction. - -## Create Batch - -```python { .api } -def create( - self, - *, - requests: list[MessageBatchIndividualRequest], - **kwargs -) -> MessageBatch: - """ - Create a batch of message requests. - - Parameters: - requests: List of individual message requests with custom_id - - Returns: - MessageBatch with id, processing_status, request counts - """ - ... - -async def create(...) -> MessageBatch: ... -``` - -## Retrieve Batch - -```python { .api } -def retrieve( - self, - message_batch_id: str, - **kwargs -) -> MessageBatch: - """Retrieve batch status and metadata.""" - ... - -async def retrieve(...) -> MessageBatch: ... -``` - -## List Batches - -```python { .api } -def list( - self, - *, - before_id: str = NOT_GIVEN, - after_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - **kwargs -) -> SyncPage[MessageBatch]: - """List batches with pagination.""" - ... - -def list(...) -> AsyncPage[MessageBatch]: ... -``` - -## Cancel Batch - -```python { .api } -def cancel( - self, - message_batch_id: str, - **kwargs -) -> MessageBatch: - """Cancel a batch in progress.""" - ... - -async def cancel(...) -> MessageBatch: ... -``` - -## Delete Batch - -```python { .api } -def delete( - self, - message_batch_id: str, - **kwargs -) -> DeletedMessageBatch: - """Delete a batch.""" - ... - -async def delete(...) -> DeletedMessageBatch: ... -``` - -## Get Results - -```python { .api } -def results( - self, - message_batch_id: str, - **kwargs -) -> JSONLDecoder[MessageBatchIndividualResponse]: - """Stream batch results as JSONL.""" - ... - -def results(...) -> AsyncJSONLDecoder[MessageBatchIndividualResponse]: ... -``` - -## Response Types - -```python { .api } -class MessageBatch(BaseModel): - """Batch metadata and status.""" - id: str - type: Literal["message_batch"] - processing_status: Literal["in_progress", "canceling", "ended"] - request_counts: MessageBatchRequestCounts - ended_at: str | None - created_at: str - expires_at: str - cancel_initiated_at: str | None - results_url: str | None - -class MessageBatchRequestCounts(BaseModel): - """Request count statistics.""" - processing: int - succeeded: int - errored: int - canceled: int - expired: int -``` - -## Request Types - -```python { .api } -class MessageBatchIndividualRequest(TypedDict): - """Individual request in batch.""" - custom_id: str - params: MessageCreateParams -``` - -## Quick Examples - -### Create Basic Batch - -```python -batch = client.messages.batches.create( - requests=[ - { - "custom_id": "request-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": "What is AI?"}] - } - }, - { - "custom_id": "request-2", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": "What is ML?"}] - } - } - ] -) -print(f"Batch ID: {batch.id}") -``` - -### Check Status - -```python -batch = client.messages.batches.retrieve("batch_abc123") -print(f"Status: {batch.processing_status}") -print(f"Succeeded: {batch.request_counts.succeeded}") -print(f"Errored: {batch.request_counts.errored}") -``` - -### Poll Until Complete - -```python -import time - -while True: - batch = client.messages.batches.retrieve(batch_id) - if batch.processing_status == "ended": - break - time.sleep(60) -``` - -### Get Results - -```python -results = client.messages.batches.results("batch_abc123") - -for response in results: - if response.result.type == "succeeded": - print(f"{response.custom_id}: {response.result.message.content[0].text}") - elif response.result.type == "errored": - print(f"{response.custom_id}: Error - {response.result.error.message}") -``` - -## See Also - -- [Messages API](./messages.md) - Core message creation -- [Batch Processing Guide](../guides/batch-processing.md) - Advanced batch patterns -- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md deleted file mode 100644 index 60718054f..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/api/completions.md +++ /dev/null @@ -1,285 +0,0 @@ -# Completions API Reference (Legacy) - -The Text Completions API is the legacy interface for text generation with Claude. For new applications, use the [Messages API](./messages.md) instead, which provides better conversation handling and additional features. - -## Overview - -The Completions API generates text based on a prompt string. It uses special prompt markers (`HUMAN_PROMPT` and `AI_PROMPT`) to structure conversations. - -**Deprecation Notice**: This API is maintained for backward compatibility. New applications should use the Messages API for improved functionality and support. - -## Create Completion - -```python { .api } -def create( - self, - *, - model: str, - prompt: str, - max_tokens_to_sample: int, - stop_sequences: list[str] = NOT_GIVEN, - temperature: float = NOT_GIVEN, - top_p: float = NOT_GIVEN, - top_k: int = NOT_GIVEN, - metadata: dict[str, Any] = NOT_GIVEN, - stream: bool = False, -) -> Completion: - """ - Create a text completion. - - Parameters: - model: Model identifier. Examples: "claude-2.1", "claude-instant-1.2" - prompt: Formatted prompt string with HUMAN_PROMPT and AI_PROMPT markers - max_tokens_to_sample: Maximum tokens to generate (required) - stop_sequences: List of sequences that stop generation when encountered - temperature: Sampling temperature 0.0-1.0 (default: 1.0) - top_p: Nucleus sampling parameter 0.0-1.0 - top_k: Top-k sampling parameter - metadata: Request metadata for tracking - stream: Enable streaming responses - - Returns: - Completion: Response containing generated text and stop reason - - Raises: - BadRequestError: Invalid request parameters - AuthenticationError: Invalid or missing API key - RateLimitError: Rate limit exceeded - """ - ... - -async def create(...) -> Completion: - """ - Async version of create. - - Same parameters and returns as synchronous create(), but executes asynchronously. - """ - ... -``` - -## Stream Completion - -```python { .api } -def stream( - self, - *, - model: str, - prompt: str, - max_tokens_to_sample: int, - **kwargs -) -> Iterator[Completion]: - """ - Stream a completion response. - - Yields partial completion objects as text is generated. - - Parameters: - Same as create() method - - Yields: - Completion objects with incremental text - - Example: - for chunk in client.completions.stream( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} Hello{AI_PROMPT}", - max_tokens_to_sample=100 - ): - print(chunk.completion, end="", flush=True) - """ - ... - -async def stream(...) -> AsyncIterator[Completion]: - """Async version of stream().""" - ... -``` - -## Response Type - -```python { .api } -class Completion(BaseModel): - """ - Text completion response. - - Attributes: - id: Unique completion identifier - type: Always "completion" - completion: Generated text content - stop_reason: Why generation stopped ("stop_sequence", "max_tokens", or "end_turn") - model: Model used for generation - """ - id: str - type: Literal["completion"] - completion: str - stop_reason: str | None - model: str -``` - -## Usage Examples - -### Basic Completion - -```python -from anthropic import Anthropic, HUMAN_PROMPT, AI_PROMPT - -client = Anthropic() - -completion = client.completions.create( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} What is the capital of France?{AI_PROMPT}", - max_tokens_to_sample=100 -) - -print(completion.completion) -``` - -### Multi-turn Conversation - -```python -# Build conversation with prompt markers -conversation = f"""{HUMAN_PROMPT} Hello, my name is Alice.{AI_PROMPT} Hi Alice! Nice to meet you.{HUMAN_PROMPT} What's my name?{AI_PROMPT}""" - -completion = client.completions.create( - model="claude-2.1", - prompt=conversation, - max_tokens_to_sample=50 -) - -print(completion.completion) -# Output: "Your name is Alice." -``` - -### Streaming Completion - -```python -prompt = f"{HUMAN_PROMPT} Write a short story about a robot.{AI_PROMPT}" - -for chunk in client.completions.stream( - model="claude-2.1", - prompt=prompt, - max_tokens_to_sample=500 -): - print(chunk.completion, end="", flush=True) -``` - -### With Stop Sequences - -```python -completion = client.completions.create( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} List three colors:{AI_PROMPT}", - max_tokens_to_sample=100, - stop_sequences=["\n\n", "4."] # Stop after listing 3 items -) -``` - -### Temperature Control - -```python -# More deterministic (lower temperature) -completion = client.completions.create( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} What is 2+2?{AI_PROMPT}", - max_tokens_to_sample=10, - temperature=0.0 -) - -# More creative (higher temperature) -creative = client.completions.create( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} Write a creative story opening.{AI_PROMPT}", - max_tokens_to_sample=200, - temperature=1.0 -) -``` - -### Async Completion - -```python -import asyncio - -async def generate(): - client = AsyncAnthropic() - - completion = await client.completions.create( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} Hello{AI_PROMPT}", - max_tokens_to_sample=50 - ) - - return completion.completion - -result = asyncio.run(generate()) -``` - -### Async Streaming - -```python -async def stream_completion(): - client = AsyncAnthropic() - - async for chunk in client.completions.stream( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} Tell me a joke{AI_PROMPT}", - max_tokens_to_sample=200 - ): - print(chunk.completion, end="", flush=True) - -asyncio.run(stream_completion()) -``` - -## Migration to Messages API - -The Messages API provides better conversation handling and additional features. Here's how to migrate: - -### Completions API (Legacy) - -```python -from anthropic import HUMAN_PROMPT, AI_PROMPT - -completion = client.completions.create( - model="claude-2.1", - prompt=f"{HUMAN_PROMPT} Hello{AI_PROMPT}", - max_tokens_to_sample=100 -) -print(completion.completion) -``` - -### Messages API (Recommended) - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=100, - messages=[{"role": "user", "content": "Hello"}] -) -print(message.content[0].text) -``` - -### Benefits of Messages API - -- Better conversation structure (no manual prompt markers) -- Support for system prompts -- Multimodal input (images, documents) -- Tool/function calling -- Better type safety -- Streaming helpers -- Token counting utilities - -## Constants - -```python { .api } -from anthropic import HUMAN_PROMPT, AI_PROMPT - -HUMAN_PROMPT: str = "\n\nHuman:" # Marker for user messages -AI_PROMPT: str = "\n\nAssistant:" # Marker for assistant responses -``` - -These constants are used to structure prompts in the Completions API format. - -## See Also - -- [Messages API](./messages.md) - Modern conversation API (recommended) -- [Streaming API](./streaming.md) - Streaming patterns and helpers -- [Client Configuration](../reference/client-config.md) - Client setup -- [Package Constants](../index.md#package-constants) - All SDK constants diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md deleted file mode 100644 index c17da9903..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/api/messages.md +++ /dev/null @@ -1,716 +0,0 @@ -# Messages API Reference - -The Messages API is the primary interface for conversational interactions with Claude. - -## Create Message - -```python { .api } -def create( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - system: str | list[TextBlockParam] = NOT_GIVEN, - metadata: MetadataParam = NOT_GIVEN, - stop_sequences: list[str] = NOT_GIVEN, - stream: bool = False, - temperature: float = NOT_GIVEN, - top_p: float = NOT_GIVEN, - top_k: int = NOT_GIVEN, - tools: list[ToolParam] = NOT_GIVEN, - tool_choice: ToolChoice = NOT_GIVEN, - service_tier: Literal["auto", "standard_only"] = NOT_GIVEN, - thinking: ThinkingConfigParam = NOT_GIVEN, -) -> Message: - """ - Create a message with Claude. - - Parameters: - model: Model identifier (required). Examples: "claude-sonnet-4-5-20250929", - "claude-opus-4-5-20250929" - messages: List of conversation messages alternating between "user" and "assistant" - roles (required). Must start with user message. - max_tokens: Maximum number of tokens to generate (required). Must be positive integer. - system: System prompt to guide Claude's behavior. Can be string or list of text blocks - with cache control. Use for setting persona, instructions, or context. - metadata: Request metadata containing user_id for tracking and abuse prevention. - stop_sequences: List of strings that will stop generation when encountered. - Up to 4 sequences allowed. - stream: Enable streaming responses. If true, use stream() method instead for - better streaming helpers. - temperature: Sampling temperature 0.0-1.0 (default: 1.0). Lower values = more - deterministic, higher values = more creative. - top_p: Nucleus sampling parameter 0.0-1.0. Cumulative probability threshold - for token selection. - top_k: Top-k sampling parameter. Only sample from top K most likely tokens. - tools: List of tools Claude can use. Enables function calling capabilities. - tool_choice: Control how Claude selects tools. Options: "auto" (default), "any" - (force tool use), "none" (disable tools), or specific tool name. - service_tier: Service tier selection. "auto" (default) or "standard_only" for - guaranteed standard capacity. - thinking: Extended thinking configuration (beta). Enables enhanced reasoning with - budget control. - - Returns: - Message: Complete message response from Claude containing: - - id: Unique message identifier - - content: List of text and/or tool_use blocks - - role: Always "assistant" - - stop_reason: Why generation stopped - - usage: Token usage statistics - - model: Model that generated response - - Raises: - BadRequestError: Invalid request parameters (e.g., empty messages, invalid model) - AuthenticationError: Invalid or missing API key - PermissionDeniedError: API key lacks required permissions - RateLimitError: Rate limit exceeded. Check retry-after header. - RequestTooLargeError: Request payload exceeds size limits - InternalServerError: Anthropic server error. Safe to retry. - OverloadedError: Service temporarily overloaded. Retry with backoff. - """ - ... - -async def create(...) -> Message: - """ - Async version of create. - - Same parameters and returns as synchronous create(), but executes asynchronously. - """ - ... -``` - -## Stream Message - -```python { .api } -def stream( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - **kwargs -) -> MessageStreamManager: - """ - Stream a message response with helper utilities. - - Provides a context manager that handles streaming with convenient helpers - for text accumulation, final message access, and event handling. - - Parameters: - model: Model identifier (required) - messages: List of conversation messages (required) - max_tokens: Maximum tokens to generate (required) - **kwargs: All other parameters from create() method supported - - Returns: - MessageStreamManager: Context manager that yields MessageStreamEvent objects. - Provides helpers: - - .text_stream: Iterator of text deltas only - - .get_final_message(): Get complete Message after stream ends - - .get_final_text(): Get accumulated text after stream ends - - .current_message_snapshot: Current state during streaming - - Raises: - Same exceptions as create() method - - Example: - with client.messages.stream(...) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - """ - ... - -def stream(...) -> AsyncMessageStreamManager: - """ - Async version of stream. - - Same parameters and returns as synchronous stream(), but executes asynchronously. - Use with `async with` syntax. - """ - ... -``` - -## Count Tokens - -```python { .api } -def count_tokens( - self, - *, - model: str, - messages: list[MessageParam], - system: str | list[TextBlockParam] = NOT_GIVEN, - tools: list[ToolParam] = NOT_GIVEN, - tool_choice: ToolChoice = NOT_GIVEN, - thinking: ThinkingConfigParam = NOT_GIVEN, -) -> MessageTokensCount: - """ - Count input tokens without creating a message. - - Useful for estimating costs before making API calls or checking if - input fits within model's context window. - - Parameters: - model: Model identifier (required). Token counts vary by model. - messages: List of conversation messages to count (required) - system: System prompt to include in count - tools: Tools to include in count - tool_choice: Tool choice configuration to include in count - thinking: Thinking configuration to include in count - - Returns: - MessageTokensCount: Object containing: - - input_tokens: Total number of input tokens - - Raises: - BadRequestError: Invalid request parameters - AuthenticationError: Invalid or missing API key - RateLimitError: Rate limit exceeded - - Note: - Token count is exact for billing purposes. Includes all overhead - from system prompt, tools, and message formatting. - """ - ... - -async def count_tokens(...) -> MessageTokensCount: - """ - Async version of count_tokens. - - Same parameters and returns as synchronous count_tokens(), but executes asynchronously. - """ - ... -``` - -## Response Types - -### Message - -```python { .api } -class Message(BaseModel): - """ - Complete message response from Claude. - - Attributes: - id: Unique message identifier (e.g., "msg_01XYZ...") - type: Always "message" - role: Always "assistant" for Claude's responses - content: List of content blocks (text, tool_use) in response - model: Model identifier used for generation - stop_reason: Reason generation stopped: - - "end_turn": Natural completion point - - "max_tokens": Hit max_tokens limit - - "stop_sequence": Hit custom stop sequence - - "tool_use": Model wants to use a tool - stop_sequence: Specific stop sequence that triggered completion (if applicable) - usage: Token usage statistics for billing and tracking - """ - id: str - type: Literal["message"] - role: Literal["assistant"] - content: list[ContentBlock] # TextBlock | ToolUseBlock - model: str - stop_reason: StopReason | None - stop_sequence: str | None - usage: Usage -``` - -### ContentBlock - -```python { .api } -ContentBlock = Union[TextBlock, ToolUseBlock] - -class TextBlock(BaseModel): - """ - Text content block in response. - - Attributes: - type: Always "text" - text: The text content generated by Claude - """ - type: Literal["text"] - text: str - -class ToolUseBlock(BaseModel): - """ - Tool invocation request from Claude. - - Attributes: - type: Always "tool_use" - id: Unique identifier for this tool call (use in tool_result) - name: Name of tool Claude wants to invoke - input: Tool input parameters as dict matching tool's input_schema - """ - type: Literal["tool_use"] - id: str - name: str - input: dict[str, Any] -``` - -### Usage - -```python { .api } -class Usage(BaseModel): - """ - Token usage statistics for billing and optimization. - - Attributes: - input_tokens: Number of tokens in the input (prompt, system, tools) - output_tokens: Number of tokens generated in response - cache_creation_input_tokens: Tokens used to create new cache entries - (charged at cache creation rate) - cache_read_input_tokens: Tokens read from cache (charged at reduced rate) - """ - input_tokens: int - output_tokens: int - cache_creation_input_tokens: int | None - cache_read_input_tokens: int | None -``` - -### Stop Reasons - -```python { .api } -StopReason = Literal[ - "end_turn", # Natural completion - Claude finished responding - "max_tokens", # Hit max_tokens limit - response may be incomplete - "stop_sequence", # Hit custom stop sequence - check stop_sequence field - "tool_use", # Model wants to use a tool - check content for ToolUseBlock -] -``` - -## Request Types - -### MessageParam - -```python { .api } -class MessageParam(TypedDict): - """ - User or assistant message in conversation. - - Fields: - role: Message sender - "user" for input, "assistant" for Claude's responses - content: Message content - string for simple text, or list of content blocks - for multimodal (text, images, documents) or tool-related content - """ - role: Literal["user", "assistant"] - content: str | list[ContentBlockParam] -``` - -### ContentBlockParam - -```python { .api } -ContentBlockParam = Union[ - TextBlockParam, - ImageBlockParam, - DocumentBlockParam, - ToolUseBlockParam, - ToolResultBlockParam, -] - -class TextBlockParam(TypedDict): - """ - Text content in user or assistant message. - - Fields: - type: Always "text" - text: The text content - cache_control: Optional prompt caching configuration. Use {"type": "ephemeral"} - to cache this content block for subsequent requests - """ - type: Literal["text"] - text: str - cache_control: NotRequired[CacheControlEphemeral] - -class ImageBlockParam(TypedDict): - """ - Image content in user message for vision capabilities. - - Fields: - type: Always "image" - source: Image source - base64-encoded data or URL - cache_control: Optional prompt caching for image - """ - type: Literal["image"] - source: Base64ImageSource | URLImageSource - cache_control: NotRequired[CacheControlEphemeral] - -class DocumentBlockParam(TypedDict): - """ - Document content (PDF or text) in user message. - - Fields: - type: Always "document" - source: Document source - base64-encoded PDF/text or URL - cache_control: Optional prompt caching for document - """ - type: Literal["document"] - source: Base64PDFSource | URLPDFSource | PlainTextSource - cache_control: NotRequired[CacheControlEphemeral] - -class ToolUseBlockParam(TypedDict): - """ - Tool invocation in assistant message (when echoing Claude's tool request). - - Fields: - type: Always "tool_use" - id: Tool call identifier from Claude's response - name: Tool name that was invoked - input: Tool input parameters as dict - cache_control: Optional prompt caching - """ - type: Literal["tool_use"] - id: str - name: str - input: dict[str, Any] - cache_control: NotRequired[CacheControlEphemeral] - -class ToolResultBlockParam(TypedDict): - """ - Tool execution result in user message. - - Fields: - type: Always "tool_result" - tool_use_id: ID from Claude's ToolUseBlock that this result corresponds to - content: Tool result - string for simple output, or list of text/image blocks - for structured results - is_error: Set to True if tool execution failed, False otherwise (default: False) - cache_control: Optional prompt caching - """ - type: Literal["tool_result"] - tool_use_id: str - content: NotRequired[str | list[TextBlockParam | ImageBlockParam]] - is_error: NotRequired[bool] - cache_control: NotRequired[CacheControlEphemeral] -``` - -### Source Types - -```python { .api } -class Base64ImageSource(TypedDict): - """ - Base64-encoded image source for vision input. - - Fields: - type: Always "base64" - media_type: Image MIME type - jpeg, png, gif, or webp - data: Base64-encoded image bytes (without data URL prefix) - """ - type: Literal["base64"] - media_type: Literal["image/jpeg", "image/png", "image/gif", "image/webp"] - data: str - -class URLImageSource(TypedDict): - """ - Image from URL for vision input. - - Fields: - type: Always "url" - url: Publicly accessible image URL (must be accessible to Anthropic servers) - """ - type: Literal["url"] - url: str - -class Base64PDFSource(TypedDict): - """ - Base64-encoded PDF document source. - - Fields: - type: Always "base64" - media_type: Always "application/pdf" - data: Base64-encoded PDF bytes (without data URL prefix) - """ - type: Literal["base64"] - media_type: Literal["application/pdf"] - data: str - -class URLPDFSource(TypedDict): - """ - PDF from URL for document processing. - - Fields: - type: Always "url" - media_type: Always "application/pdf" - url: Publicly accessible PDF URL - """ - type: Literal["url"] - media_type: Literal["application/pdf"] - url: str - -class PlainTextSource(TypedDict): - """ - Plain text document source. - - Fields: - type: Always "text" - media_type: Always "text/plain" - data: Plain text content - """ - type: Literal["text"] - media_type: Literal["text/plain"] - data: str -``` - -### Configuration Types - -```python { .api } -class MetadataParam(TypedDict, total=False): - """ - Request metadata for tracking and compliance. - - Fields: - user_id: End-user identifier for tracking, rate limiting, and abuse prevention. - Should be unique per end-user, not per API key. - """ - user_id: str - -class CacheControlEphemeral(TypedDict): - """ - Prompt caching configuration for reducing costs on repeated content. - - Fields: - type: Always "ephemeral" - content cached temporarily (~5 minutes) - """ - type: Literal["ephemeral"] - -class MessageTokensCount(BaseModel): - """ - Token count response from count_tokens(). - - Attributes: - input_tokens: Exact number of input tokens for billing estimation - """ - input_tokens: int -``` - -## Quick Examples - -### Basic Text - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[ - {"role": "user", "content": "Hello"} - ] -) -print(message.content[0].text) -``` - -### System Prompt - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - system="You are a helpful assistant.", - messages=[ - {"role": "user", "content": "Hello"} - ] -) -``` - -### Multi-Turn Conversation - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[ - {"role": "user", "content": "My name is Alice."}, - {"role": "assistant", "content": "Hello Alice! Nice to meet you."}, - {"role": "user", "content": "What's my name?"} - ] -) -``` - -### Image Analysis - -```python -import base64 - -with open("image.jpg", "rb") as f: - image_data = base64.standard_b64encode(f.read()).decode() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "image", - "source": { - "type": "base64", - "media_type": "image/jpeg", - "data": image_data - } - }, - {"type": "text", "text": "What's in this image?"} - ] - }] -) -``` - -### Document Processing - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": pdf_base64_data - } - }, - {"type": "text", "text": "Summarize this document."} - ] - }] -) -``` - -### Temperature Control - -```python -# Deterministic (temperature = 0) -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - temperature=0.0, - messages=[{"role": "user", "content": "What is 2+2?"}] -) - -# Creative (temperature = 1) -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - temperature=1.0, - messages=[{"role": "user", "content": "Write a creative story."}] -) -``` - -### Prompt Caching - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - system=[ - { - "type": "text", - "text": "You are an expert on Shakespeare.", - "cache_control": {"type": "ephemeral"} - } - ], - messages=[ - {"role": "user", "content": "Tell me about Hamlet."} - ] -) - -# Check cache usage -print(f"Cache creation: {message.usage.cache_creation_input_tokens}") -print(f"Cache read: {message.usage.cache_read_input_tokens}") -``` - -### Token Counting - -```python -token_count = client.messages.count_tokens( - model="claude-sonnet-4-5-20250929", - messages=[ - {"role": "user", "content": "What is the capital of France?"} - ] -) -print(f"Input tokens: {token_count.input_tokens}") -``` - -### Metadata Tracking - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - metadata={"user_id": "user_12345"}, - messages=[{"role": "user", "content": "Hello"}] -) -``` - -## Raw Response Access - -Access raw HTTP responses for headers, status codes, and debugging. - -```python { .api } -# Access raw response wrapper -client.messages.with_raw_response.create(...) # Returns APIResponse[Message] -client.messages.with_streaming_response.stream(...) # Returns APIResponse with streaming - -# Async versions -async_client.messages.with_raw_response.create(...) -async_client.messages.with_streaming_response.stream(...) -``` - -### Raw Response Example - -```python -# Get raw HTTP response -response = client.messages.with_raw_response.create( - model="claude-sonnet-4-5-20250929", - max_tokens=100, - messages=[{"role": "user", "content": "Hello"}] -) - -# Access HTTP response details -print(f"Status: {response.http_response.status_code}") -print(f"Headers: {response.http_response.headers}") -print(f"Request ID: {response.http_response.headers.get('request-id')}") - -# Access parsed message object -message = response.parse() -print(message.content[0].text) -``` - -### Streaming Raw Response - -```python -# Get raw streaming response -with client.messages.with_streaming_response.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=100, - messages=[{"role": "user", "content": "Hello"}] -) as response: - # Access response object before streaming - print(f"Status: {response.http_response.status_code}") - - # Stream normally - for text in response.text_stream: - print(text, end="", flush=True) -``` - -### Check Rate Limit Headers - -```python -response = client.messages.with_raw_response.create( - model="claude-sonnet-4-5-20250929", - max_tokens=100, - messages=[{"role": "user", "content": "Hello"}] -) - -headers = response.http_response.headers -print(f"Rate limit: {headers.get('anthropic-ratelimit-requests-limit')}") -print(f"Remaining: {headers.get('anthropic-ratelimit-requests-remaining')}") -print(f"Reset: {headers.get('anthropic-ratelimit-requests-reset')}") -``` - -## See Also - -- [Streaming API](./streaming.md) - Real-time response streaming -- [Tool Use API](./tools.md) - Function calling capabilities -- [Multimodal Guide](../guides/multimodal.md) - Images and documents -- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/models.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/models.md deleted file mode 100644 index c3224a326..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/api/models.md +++ /dev/null @@ -1,161 +0,0 @@ -# Models API Reference - -Retrieve information about available Claude models and their capabilities. - -## Retrieve Model - -```python { .api } -def retrieve( - self, - model_id: str, - **kwargs -) -> ModelInfo: - """ - Retrieve model information. - - Parameters: - model_id: Model identifier (e.g., "claude-sonnet-4-5-20250929") - - Returns: - ModelInfo with model metadata and capabilities - """ - ... - -async def retrieve(...) -> ModelInfo: ... -``` - -## List Models - -```python { .api } -def list( - self, - *, - before_id: str = NOT_GIVEN, - after_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - **kwargs -) -> SyncPage[ModelInfo]: - """ - List available models with pagination. - - Returns: - SyncPage of ModelInfo objects with auto-pagination - """ - ... - -def list(...) -> AsyncPage[ModelInfo]: ... -``` - -## Response Type - -```python { .api } -class ModelInfo(BaseModel): - """Model information and capabilities.""" - id: str - type: Literal["model"] - display_name: str - created_at: str -``` - -## Available Models - -### Claude 4.5 (Latest) - -```python { .api } -"claude-opus-4-5-20250929" # Most capable model -"claude-sonnet-4-5-20250929" # Balanced intelligence and speed -``` - -### Claude 3.5 - -```python { .api } -"claude-3-5-sonnet-20241022" # Previous Sonnet version -"claude-3-5-sonnet-20240620" # Earlier Sonnet version -"claude-3-5-haiku-20241022" # Fast, cost-effective -``` - -### Claude 3 - -```python { .api } -"claude-3-opus-20240229" # Powerful, intelligent -"claude-3-sonnet-20240229" # Balanced -"claude-3-haiku-20240307" # Fast and efficient -``` - -### Legacy (Claude 2) - -```python { .api } -"claude-2.1" # Legacy Claude 2.1 -"claude-2.0" # Legacy Claude 2.0 -"claude-instant-1.2" # Legacy instant model -``` - -## Quick Examples - -### Retrieve Specific Model - -```python -model = client.models.retrieve("claude-sonnet-4-5-20250929") -print(f"Model: {model.display_name}") -print(f"Created: {model.created_at}") -``` - -### List All Models - -```python -for model in client.models.list(): - print(f"{model.id}: {model.display_name}") -``` - -### Check Model Availability - -```python -from anthropic import NotFoundError - -def model_exists(model_id: str) -> bool: - try: - client.models.retrieve(model_id) - return True - except NotFoundError: - return False - -if model_exists("claude-sonnet-4-5-20250929"): - print("Model is available") -``` - -### Filter Models by Family - -```python -def get_models_by_family(family: str) -> list[ModelInfo]: - """Get all models in a family (e.g., 'sonnet', 'opus', 'haiku').""" - return [m for m in client.models.list() if family.lower() in m.id.lower()] - -sonnet_models = get_models_by_family("sonnet") -for model in sonnet_models: - print(model.display_name) -``` - -### Model Selection Helper - -```python -def select_model(capability: str = "balanced") -> str: - """ - Select appropriate model based on requirements. - - Args: - capability: "maximum" (opus), "balanced" (sonnet), "fast" (haiku) - """ - if capability == "maximum": - return "claude-opus-4-5-20250929" - elif capability == "fast": - return "claude-3-5-haiku-20241022" - else: # balanced - return "claude-sonnet-4-5-20250929" - -model_id = select_model("balanced") -``` - -## See Also - -- [Messages API](./messages.md) - Core message creation -- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md deleted file mode 100644 index 76fda7b7a..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/api/streaming.md +++ /dev/null @@ -1,838 +0,0 @@ -# Streaming API Reference - -Stream message responses with rich event handling and helper utilities for incremental processing. - -## Stream Message - -```python { .api } -def stream( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - system: str | list[TextBlockParam] = NOT_GIVEN, - temperature: float = NOT_GIVEN, - tools: list[ToolParam] = NOT_GIVEN, - **kwargs -) -> MessageStreamManager: - """ - Stream a message response with helper utilities. - - Returns a context manager that provides streaming with convenient helpers - for text accumulation, event handling, and final message access. - - Parameters: - model: Model identifier (required) - messages: List of conversation messages (required) - max_tokens: Maximum tokens to generate (required) - system: System prompt (string or list with cache control) - temperature: Sampling temperature 0.0-1.0 - tools: Available tools for function calling - **kwargs: All other parameters from messages.create() are supported - - Returns: - MessageStreamManager: Context manager that provides: - - MessageStream for iteration - - Helper methods for text and final message extraction - - Raises: - Same exceptions as messages.create() - - Example: - with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - """ - ... - -async def stream( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - **kwargs -) -> AsyncMessageStreamManager: - """ - Async version of stream. - - Same parameters and behavior as synchronous stream(), but executes - asynchronously. Use with `async with` syntax. - - Returns: - AsyncMessageStreamManager: Async context manager for streaming - """ - ... -``` - -## MessageStream - -```python { .api } -class MessageStream: - """ - Synchronous message stream with rich event handling and helper methods. - - Provides convenient access to streaming events, text deltas, and final message - accumulation. Use via MessageStreamManager context manager. - """ - - def __iter__(self) -> Iterator[MessageStreamEvent]: - """ - Iterate over all stream events. - - Yields: - MessageStreamEvent: Events including message_start, content_block_start, - content_block_delta, content_block_stop, message_delta, message_stop - """ - ... - - def __enter__(self) -> MessageStream: - """Context manager entry. Returns self.""" - ... - - def __exit__(self, exc_type, exc_val, exc_tb) -> None: - """Context manager exit. Closes stream and cleans up resources.""" - ... - - def get_final_message(self) -> Message: - """ - Get final accumulated message after stream completes. - - Must be called after iterating through the stream. Returns complete Message - object with all content blocks, usage statistics, and stop reason. - - Returns: - Message: Complete message with all accumulated content - - Raises: - RuntimeError: If called before stream completes - """ - ... - - def get_final_text(self) -> str: - """ - Get final accumulated text from all text blocks. - - Concatenates text from all TextBlock content blocks in the message. - Useful for simple text-only responses. - - Returns: - str: Concatenated text from all text blocks - - Raises: - RuntimeError: If called before stream completes - """ - ... - - @property - def text_stream(self) -> Iterator[str]: - """ - Iterate over text deltas only, filtering out other events. - - Convenient property for streaming text to console or UI. Automatically - extracts text from content_block_delta events. - - Yields: - str: Text delta strings as they arrive - - Example: - for text in stream.text_stream: - print(text, end="", flush=True) - """ - ... - - @property - def current_message_snapshot(self) -> Message: - """ - Get current accumulated message snapshot during streaming. - - Provides partial Message object with content accumulated so far. Useful - for showing partial results or implementing custom UI updates. - - Returns: - Message: Partial message with current content (usage may be incomplete) - """ - ... - -class AsyncMessageStream: - """ - Asynchronous message stream with rich event handling and helper methods. - - Async version of MessageStream. All methods are async and use async iteration. - """ - - def __aiter__(self) -> AsyncIterator[MessageStreamEvent]: - """ - Async iterate over all stream events. - - Yields: - MessageStreamEvent: Events as they arrive from the API - """ - ... - - async def __aenter__(self) -> AsyncMessageStream: - """Async context manager entry. Returns self.""" - ... - - async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: - """Async context manager exit. Closes stream and cleans up resources.""" - ... - - async def get_final_message(self) -> Message: - """ - Get final accumulated message after stream completes. - - Returns: - Message: Complete message with all accumulated content - """ - ... - - async def get_final_text(self) -> str: - """ - Get final accumulated text from all text blocks. - - Returns: - str: Concatenated text from all text blocks - """ - ... - - @property - def text_stream(self) -> AsyncIterator[str]: - """ - Async iterate over text deltas only. - - Yields: - str: Text delta strings as they arrive - """ - ... - - @property - def current_message_snapshot(self) -> Message: - """ - Get current accumulated message snapshot during streaming. - - Returns: - Message: Partial message with current content - """ - ... -``` - -## MessageStreamManager - -Context manager for streaming messages with helper methods. - -```python { .api } -class MessageStreamManager: - """ - Synchronous context manager for message streaming. - - Provides: - - Context manager protocol - - Access to MessageStream with helpers - """ - def __enter__(self) -> MessageStream: - """Enter context and return stream.""" - ... - - def __exit__(self, exc_type, exc_val, exc_tb) -> None: - """Exit context and cleanup.""" - ... - -class AsyncMessageStreamManager: - """ - Asynchronous context manager for message streaming. - - Provides: - - Async context manager protocol - - Access to AsyncMessageStream with helpers - """ - async def __aenter__(self) -> AsyncMessageStream: - """Enter async context and return stream.""" - ... - - async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: - """Exit async context and cleanup.""" - ... -``` - -## Base Stream Classes - -For advanced use cases requiring raw SSE event access. - -```python { .api } -class Stream(Generic[T]): - """ - Base synchronous stream for raw SSE events. - - Provides: - - Raw event iteration - - Response access - """ - def __iter__(self) -> Iterator[T]: - """Iterate over stream items.""" - ... - - def __enter__(self) -> Stream[T]: - """Context manager entry.""" - ... - - def __exit__(self, exc_type, exc_val, exc_tb) -> None: - """Context manager exit.""" - ... - - @property - def response(self) -> httpx.Response: - """Access raw HTTP response.""" - ... - -class AsyncStream(Generic[T]): - """ - Base asynchronous stream for raw SSE events. - - Provides: - - Async raw event iteration - - Response access - """ - def __aiter__(self) -> AsyncIterator[T]: - """Async iterate over stream items.""" - ... - - async def __aenter__(self) -> AsyncStream[T]: - """Async context manager entry.""" - ... - - async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: - """Async context manager exit.""" - ... - - @property - def response(self) -> httpx.Response: - """Access raw HTTP response.""" - ... -``` - -## Stream Events - -```python { .api } -MessageStreamEvent = Union[ - MessageStartEvent, # Stream started - MessageDeltaEvent, # Usage or stop_reason updated - MessageStopEvent, # Stream completed - ContentBlockStartEvent, # New content block begins - ContentBlockDeltaEvent, # Content delta received - ContentBlockStopEvent, # Content block completed -] -``` - -### Message Lifecycle Events - -```python { .api } -class MessageStartEvent(BaseModel): - """ - Stream started with initial message metadata. - - First event in every stream. Contains message skeleton with empty content. - - Attributes: - type: Always "message_start" - message: Initial Message object with id, role, model, but empty content - """ - type: Literal["message_start"] - message: Message - -class MessageDeltaEvent(BaseModel): - """ - Message metadata updated (stop_reason or usage). - - Sent near end of stream when stop_reason is determined or final usage - statistics are available. - - Attributes: - type: Always "message_delta" - delta: Changed message fields (stop_reason, stop_sequence) - usage: Updated token usage (output_tokens) - """ - type: Literal["message_delta"] - delta: MessageDelta - usage: MessageDeltaUsage - -class MessageDelta(BaseModel): - """ - Changed message fields in MessageDeltaEvent. - - Attributes: - stop_reason: Why generation stopped - "end_turn", "max_tokens", - "stop_sequence", or "tool_use" - stop_sequence: Stop sequence that triggered completion (if applicable) - """ - stop_reason: StopReason | None - stop_sequence: str | None - -class MessageDeltaUsage(BaseModel): - """ - Token usage update in MessageDeltaEvent. - - Attributes: - output_tokens: Total output tokens generated so far - """ - output_tokens: int - -class MessageStopEvent(BaseModel): - """ - Stream completed successfully. - - Final event in every successful stream. After this event, stream is closed - and final message is available via get_final_message(). - - Attributes: - type: Always "message_stop" - """ - type: Literal["message_stop"] -``` - -### Content Block Events - -```python { .api } -class ContentBlockStartEvent(BaseModel): - """ - New content block started in response. - - Sent when Claude begins generating a new content block (text or tool_use). - Contains initial empty block structure. - - Attributes: - type: Always "content_block_start" - index: Zero-based index of this content block in message.content list - content_block: Initial ContentBlock (TextBlock or ToolUseBlock) with - empty/default values - """ - type: Literal["content_block_start"] - index: int - content_block: ContentBlock - -class ContentBlockDeltaEvent(BaseModel): - """ - Content block received incremental update. - - Most frequent event type. Contains incremental content (text deltas or - JSON deltas for tool inputs). - - Attributes: - type: Always "content_block_delta" - index: Zero-based index of content block being updated - delta: Delta content - TextDelta for text blocks, InputJSONDelta for - tool_use blocks - """ - type: Literal["content_block_delta"] - index: int - delta: ContentBlockDelta - -ContentBlockDelta = Union[TextDelta, InputJSONDelta] - -class TextDelta(BaseModel): - """ - Text content delta for TextBlock. - - Attributes: - type: Always "text_delta" - text: Incremental text to append to current text block - """ - type: Literal["text_delta"] - text: str - -class InputJSONDelta(BaseModel): - """ - Tool input JSON delta for ToolUseBlock. - - Attributes: - type: Always "input_json_delta" - partial_json: Incremental JSON string to append. May be incomplete JSON - until block completes. - """ - type: Literal["input_json_delta"] - partial_json: str - -class ContentBlockStopEvent(BaseModel): - """ - Content block completed. - - Sent when a content block finishes. After this event, the content block - at the given index is complete. - - Attributes: - type: Always "content_block_stop" - index: Zero-based index of completed content block - """ - type: Literal["content_block_stop"] - index: int -``` - -### Raw Stream Events - -For advanced use cases requiring access to raw SSE events before parsing. - -```python { .api } -RawMessageStreamEvent = Union[ - RawMessageStartEvent, - RawMessageDeltaEvent, - RawMessageStopEvent, - RawContentBlockStartEvent, - RawContentBlockDeltaEvent, - RawContentBlockStopEvent, -] - -class RawMessageStartEvent(BaseModel): - """Raw message start event from SSE.""" - type: Literal["message_start"] - message: Message - -class RawMessageDeltaEvent(BaseModel): - """Raw message delta event from SSE.""" - type: Literal["message_delta"] - delta: dict[str, Any] - usage: MessageDeltaUsage - -class RawMessageStopEvent(BaseModel): - """Raw message stop event from SSE.""" - type: Literal["message_stop"] - -class RawContentBlockStartEvent(BaseModel): - """Raw content block start from SSE.""" - type: Literal["content_block_start"] - index: int - content_block: dict[str, Any] - -class RawContentBlockDeltaEvent(BaseModel): - """Raw content block delta from SSE.""" - type: Literal["content_block_delta"] - index: int - delta: dict[str, Any] - -class RawContentBlockStopEvent(BaseModel): - """Raw content block stop from SSE.""" - type: Literal["content_block_stop"] - index: int -``` - -## Quick Examples - -### Basic Streaming - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a short story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -print() -``` - -### Stream All Events - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) as stream: - for event in stream: - if event.type == "message_start": - print(f"Message started: {event.message.id}") - elif event.type == "content_block_start": - print(f"Content block {event.index} started") - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - elif event.type == "content_block_stop": - print(f"\nContent block {event.index} stopped") - elif event.type == "message_delta": - print(f"Stop reason: {event.delta.stop_reason}") - elif event.type == "message_stop": - print("Message stopped") -``` - -### Get Final Message - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "What is 2+2?"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - -message = stream.get_final_message() -print(f"\nTotal tokens: {message.usage.output_tokens}") -``` - -### Stream with Tool Use - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[ - { - "name": "get_weather", - "description": "Get weather for a location", - "input_schema": { - "type": "object", - "properties": { - "location": {"type": "string"} - }, - "required": ["location"], - }, - } - ], - messages=[ - {"role": "user", "content": "What's the weather in San Francisco?"} - ], -) as stream: - for event in stream: - if event.type == "content_block_start": - if event.content_block.type == "tool_use": - print(f"Tool call: {event.content_block.name}") - elif event.type == "content_block_delta": - if event.delta.type == "input_json_delta": - print(event.delta.partial_json, end="") - -message = stream.get_final_message() - -# Process tool calls -for block in message.content: - if block.type == "tool_use": - print(f"Tool: {block.name}") - print(f"Input: {block.input}") -``` - -### Async Streaming - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - async with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a haiku"}] - ) as stream: - async for text in stream.text_stream: - print(text, end="", flush=True) - print() - -asyncio.run(main()) -``` - -### Get Final Text Only - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) as stream: - # Consume stream - for _ in stream: - pass - -# Get accumulated text -text = stream.get_final_text() -print(text) -``` - -### Stream with Current Snapshot - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Count to 10"}] -) as stream: - for event in stream: - # Get current accumulated message - current = stream.current_message_snapshot - if current.content: - print(f"Current text length: {len(current.content[0].text)}") -``` - -### Async Event Processing - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def stream_with_events(): - client = AsyncAnthropic() - - async with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) as stream: - async for event in stream: - print(f"Event: {event.type}") - -asyncio.run(stream_with_events()) -``` - -### Error Handling in Streams - -```python -from anthropic import APIError - -try: - with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -except APIError as e: - print(f"Stream error: {e}") -``` - -### Manual Stream Iteration - -```python -stream_manager = client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) - -stream = stream_manager.__enter__() - -try: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) -finally: - stream_manager.__exit__(None, None, None) -``` - -### Streaming with Temperature - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - temperature=0.8, - messages=[{"role": "user", "content": "Write a creative story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -### Stream Multi-Turn Conversation - -```python -conversation = [ - {"role": "user", "content": "Hi, I'm Alice"}, -] - -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=conversation, -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - - message = stream.get_final_message() - -# Add to conversation -conversation.append({ - "role": "assistant", - "content": message.content, -}) -conversation.append({ - "role": "user", - "content": "What's my name?", -}) - -# Continue streaming -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=conversation, -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -### Access Raw HTTP Response - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) as stream: - # Access underlying HTTP response - print(f"Request ID: {stream.response.headers.get('request-id')}") - - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -### Concurrent Async Streams - -```python -async def stream_multiple(): - client = AsyncAnthropic() - - async def stream_one(prompt: str): - async with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": prompt}], - ) as stream: - text = await stream.get_final_text() - return text - - results = await asyncio.gather( - stream_one("What is 2+2?"), - stream_one("What is the capital of France?"), - stream_one("What is Python?"), - ) - - for result in results: - print(result) - print("---") - -asyncio.run(stream_multiple()) -``` - -### Track Token Usage During Streaming - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a long essay"}] -) as stream: - for event in stream: - if event.type == "message_delta": - print(f"\nTokens so far: {event.usage.output_tokens}") - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) -``` - -## See Also - -- [Messages API](./messages.md) - Core message creation -- [Streaming Guide](../guides/streaming-guide.md) - Advanced streaming patterns -- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md b/.tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md deleted file mode 100644 index f51e2e00d..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/api/tools.md +++ /dev/null @@ -1,423 +0,0 @@ -# Tool Use API Reference - -Define and use tools (function calling) with automatic schema generation from Python functions. - -## Tool Decorators - -```python { .api } -def beta_tool(func: Callable) -> BetaFunctionTool: - """ - Decorator to create a synchronous tool from Python function. - - Automatically generates JSON schema from function signature, type hints, - and docstring. Supports standard Python types, Optional, Union, List, Dict. - - Parameters: - func: Python function to convert to tool. Must have type hints and docstring. - - Returns: - BetaFunctionTool: Wrapper with auto-generated schema and callable interface - - Example: - @beta_tool - def get_weather(location: str, unit: str = "fahrenheit") -> dict: - '''Get weather for location. - - Args: - location: City and state - unit: Temperature unit - ''' - return {"temp": 72, "condition": "sunny"} - """ - ... - -def beta_async_tool(func: Callable) -> BetaAsyncFunctionTool: - """ - Decorator to create an asynchronous tool from Python async function. - - Automatically generates JSON schema from function signature, type hints, - and docstring. Use for tools that perform async I/O operations. - - Parameters: - func: Python async function to convert to tool. Must have type hints and docstring. - - Returns: - BetaAsyncFunctionTool: Wrapper with auto-generated schema and async callable interface - - Example: - @beta_async_tool - async def fetch_url(url: str) -> str: - '''Fetch content from URL. - - Args: - url: URL to fetch - ''' - async with httpx.AsyncClient() as client: - response = await client.get(url) - return response.text - """ - ... -``` - -## Tool Runner - -```python { .api } -def tool_runner( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - tools: list[ToolParam | BetaFunctionTool], - tool_choice: ToolChoice = NOT_GIVEN, - **kwargs -) -> Iterator[BetaMessage]: - """ - Run message with automatic tool execution and conversation management. - - Automatically handles the agentic loop: sends message to Claude, executes any - tool calls, sends results back, and continues until Claude responds without - tool use (stop_reason == "end_turn"). - - Parameters: - model: Model identifier (required) - messages: Initial conversation messages (required) - max_tokens: Maximum tokens per Claude response (required) - tools: List of available tools - ToolParam dicts or BetaFunctionTool/BetaAsyncFunctionTool - decorated functions - tool_choice: Control tool selection behavior - "auto" (default), "any", "none", - or specific tool name - **kwargs: Additional parameters passed to messages.create() (system, temperature, etc.) - - Yields: - BetaMessage: Message after each turn in the conversation loop. Final message - will have stop_reason "end_turn" when conversation completes. - - Raises: - Same exceptions as messages.create() - ToolExecutionError: If tool execution fails and error handling doesn't catch it - - Example: - for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather, search_db], - messages=[{"role": "user", "content": "What's the weather?"}] - ): - if message.stop_reason == "end_turn": - print(message.content[0].text) - """ - ... - -async def tool_runner( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - tools: list[ToolParam | BetaAsyncFunctionTool], - tool_choice: ToolChoice = NOT_GIVEN, - **kwargs -) -> AsyncIterator[BetaMessage]: - """ - Async version of tool_runner. - - Same parameters and behavior as synchronous tool_runner(), but executes - asynchronously and supports async tools (BetaAsyncFunctionTool). - - Yields: - BetaMessage: Message after each turn in the conversation loop - """ - ... -``` - -## Tool Types - -### ToolParam - -```python { .api } -class ToolParam(TypedDict): - """ - Manual tool definition without decorator. - - Fields: - name: Tool name (alphanumeric + underscores only). Used by Claude to identify tool. - description: Clear description of what the tool does. Claude uses this to decide - when to use the tool. - input_schema: JSON Schema (draft 2020-12) defining tool parameters. Use standard - JSON Schema types: object, string, number, boolean, array, etc. - cache_control: Optional prompt caching. Use {"type": "ephemeral"} to cache tool - definition across requests. - """ - name: str - description: str - input_schema: dict[str, Any] - cache_control: NotRequired[CacheControlEphemeral] -``` - -### ToolChoice - -```python { .api } -ToolChoice = Union[ - ToolChoiceAuto, # Let Claude decide whether to use tools (default) - ToolChoiceAny, # Force Claude to use at least one tool - ToolChoiceNone, # Disable all tool use for this request - ToolChoiceTool, # Force Claude to use specific tool by name -] - -class ToolChoiceAuto(TypedDict): - """ - Let Claude decide whether to use tools based on conversation context. - - Fields: - type: Always "auto" - disable_parallel_tool_use: Set to True to force sequential tool calls. - Default is False (parallel calls allowed). - """ - type: Literal["auto"] - disable_parallel_tool_use: NotRequired[bool] - -class ToolChoiceAny(TypedDict): - """ - Force Claude to use at least one tool (any tool from available tools). - - Useful when you want to ensure Claude performs an action rather than just - responding with text. - - Fields: - type: Always "any" - disable_parallel_tool_use: Set to True to force sequential tool calls - """ - type: Literal["any"] - disable_parallel_tool_use: NotRequired[bool] - -class ToolChoiceNone(TypedDict): - """ - Disable all tool use for this request. - - Claude will respond with text only, even if tools are provided. Useful for - forcing text responses in multi-turn conversations. - - Fields: - type: Always "none" - """ - type: Literal["none"] - -class ToolChoiceTool(TypedDict): - """ - Force Claude to use a specific tool by name. - - Claude will always use this tool in its first response. Useful for - deterministic workflows. - - Fields: - type: Always "tool" - name: Exact name of tool to use (must match tool in tools list) - disable_parallel_tool_use: Set to True to force only this tool call - """ - type: Literal["tool"] - name: str - disable_parallel_tool_use: NotRequired[bool] -``` - -### Tool Function Classes - -```python { .api } -class BetaFunctionTool: - """ - Synchronous function tool wrapper created by @beta_tool decorator. - - Attributes: - name: Tool name derived from function name - description: Tool description from function docstring - input_schema: Auto-generated JSON Schema from function signature - func: Underlying Python function - """ - name: str - description: str - input_schema: dict[str, Any] - func: Callable - - def __call__(self, **kwargs) -> Any: - """Execute tool with parameters.""" - ... - - def to_param(self) -> ToolParam: - """Convert to ToolParam dict for API calls.""" - ... - -class BetaAsyncFunctionTool: - """ - Asynchronous function tool wrapper created by @beta_async_tool decorator. - - Attributes: - name: Tool name derived from function name - description: Tool description from function docstring - input_schema: Auto-generated JSON Schema from function signature - func: Underlying Python async function - """ - name: str - description: str - input_schema: dict[str, Any] - func: Callable - - async def __call__(self, **kwargs) -> Any: - """Execute async tool with parameters.""" - ... - - def to_param(self) -> ToolParam: - """Convert to ToolParam dict for API calls.""" - ... -``` - -## Quick Examples - -### Manual Tool Definition - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[{ - "name": "get_weather", - "description": "Get weather for a location", - "input_schema": { - "type": "object", - "properties": { - "location": {"type": "string", "description": "City and state"} - }, - "required": ["location"] - } - }], - messages=[{"role": "user", "content": "What's the weather in SF?"}] -) - -for block in message.content: - if block.type == "tool_use": - print(f"Tool: {block.name}, Input: {block.input}") -``` - -### Using Tool Decorator - -```python -from anthropic import beta_tool - -@beta_tool -def get_weather(location: str, unit: str = "fahrenheit") -> dict: - """ - Get weather for a location. - - Args: - location: City and state, e.g. San Francisco, CA - unit: Temperature unit (celsius or fahrenheit) - """ - return {"location": location, "temperature": 72, "unit": unit, "condition": "sunny"} - -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather], - messages=[{"role": "user", "content": "What's the weather in NYC?"}] -) -``` - -### Tool Runner with Auto-Execution - -```python -@beta_tool -def get_weather(location: str) -> dict: - """Get weather for location.""" - return {"temp": 72, "condition": "sunny"} - -@beta_tool -def search_database(query: str) -> list: - """Search database.""" - return [{"id": 1, "name": "Result 1"}] - -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather, search_database], - messages=[{"role": "user", "content": "What's the weather in SF?"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -### Handle Tool Call Manually - -```python -# First request - Claude requests tool use -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - messages=[{"role": "user", "content": "What's the weather in Paris?"}] -) - -# Extract and execute tool call -tool_use = next(block for block in message.content if block.type == "tool_use") -weather_data = get_weather(location=tool_use.input["location"]) - -# Send result back -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - messages=[ - {"role": "user", "content": "What's the weather in Paris?"}, - {"role": "assistant", "content": message.content}, - { - "role": "user", - "content": [{ - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": str(weather_data) - }] - } - ] -) -``` - -### Force Tool Use - -```python -# Force any tool -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "any"}, - messages=[{"role": "user", "content": "Hello"}] -) - -# Force specific tool -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "tool", "name": "get_weather"}, - messages=[{"role": "user", "content": "Hello"}] -) -``` - -### Disable Parallel Tool Use - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={ - "type": "auto", - "disable_parallel_tool_use": True - }, - messages=[{"role": "user", "content": "Get weather for NYC and LA"}] -) -``` - -## See Also - -- [Messages API](./messages.md) - Core message creation -- [Tool Usage Guide](../guides/tool-usage.md) - Advanced tool patterns -- [Type System](../reference/types.md) - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md deleted file mode 100644 index 8de22d8eb..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/batches.md +++ /dev/null @@ -1,611 +0,0 @@ -# Beta Message Batches - -Process multiple beta message requests in batch mode with support for all beta features including thinking, citations, web search, code execution, and more. - -## Overview - -Beta message batches extend standard message batches with support for beta features. They provide: -- 50% cost reduction compared to standard API -- Support for all beta features (thinking, citations, web search, etc.) -- Same interface as standard batches -- Asynchronous processing of thousands of requests - -## API Reference - -### Create Batch - -```python { .api } -def create( - self, - *, - requests: Iterable[Request], - betas: list[AnthropicBetaParam] = NOT_GIVEN, - **kwargs -) -> BetaMessageBatch: - """ - Create a batch of beta message requests. - - Parameters: - requests: List of beta message creation requests - Each request includes custom_id and params dict - betas: Additional beta features to enable beyond default batch support - - Returns: - BetaMessageBatch with batch ID and processing status - """ - ... - -async def create( - self, - **kwargs -) -> BetaMessageBatch: - """Async version of create.""" - ... -``` - -### Retrieve Batch - -```python { .api } -def retrieve( - self, - message_batch_id: str, - *, - betas: list[AnthropicBetaParam] = NOT_GIVEN, - **kwargs -) -> BetaMessageBatch: - """ - Retrieve beta message batch status and metadata. - - Parameters: - message_batch_id: ID of the batch to retrieve - betas: Optional beta features header - - Returns: - BetaMessageBatch with current processing status - """ - ... - -async def retrieve( - self, - message_batch_id: str, - **kwargs -) -> BetaMessageBatch: - """Async version of retrieve.""" - ... -``` - -### List Batches - -```python { .api } -def list( - self, - *, - after_id: str = NOT_GIVEN, - before_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - betas: list[AnthropicBetaParam] = NOT_GIVEN, - **kwargs -) -> SyncPage[BetaMessageBatch]: - """ - List beta message batches with pagination. - - Parameters: - after_id: Return batches after this ID - before_id: Return batches before this ID - limit: Maximum number of batches to return (1-1000, default 20) - betas: Optional beta features header - - Returns: - Paginated list of BetaMessageBatch objects - """ - ... - -def list( - self, - **kwargs -) -> AsyncPaginator[BetaMessageBatch, AsyncPage[BetaMessageBatch]]: - """Async version of list.""" - ... -``` - -### Cancel Batch - -```python { .api } -def cancel( - self, - message_batch_id: str, - *, - betas: list[AnthropicBetaParam] = NOT_GIVEN, - **kwargs -) -> BetaMessageBatch: - """ - Cancel a beta message batch before processing completes. - - Parameters: - message_batch_id: ID of the batch to cancel - betas: Optional beta features header - - Returns: - BetaMessageBatch with canceling status - """ - ... - -async def cancel( - self, - message_batch_id: str, - **kwargs -) -> BetaMessageBatch: - """Async version of cancel.""" - ... -``` - -### Delete Batch - -```python { .api } -def delete( - self, - message_batch_id: str, - *, - betas: list[AnthropicBetaParam] = NOT_GIVEN, - **kwargs -) -> BetaDeletedMessageBatch: - """ - Delete a completed beta message batch. - - Batches must be finished processing before deletion. - Cancel in-progress batches first if needed. - - Parameters: - message_batch_id: ID of the batch to delete - betas: Optional beta features header - - Returns: - BetaDeletedMessageBatch confirming deletion - """ - ... - -async def delete( - self, - message_batch_id: str, - **kwargs -) -> BetaDeletedMessageBatch: - """Async version of delete.""" - ... -``` - -### Get Results - -```python { .api } -def results( - self, - message_batch_id: str, - *, - betas: list[AnthropicBetaParam] = NOT_GIVEN, - **kwargs -) -> JSONLDecoder[BetaMessageBatchIndividualResponse]: - """ - Stream beta message batch results as JSONL. - - Each line contains one request's result with custom_id for matching. - Results order is not guaranteed to match request order. - - Parameters: - message_batch_id: ID of the batch - betas: Optional beta features header - - Returns: - JSONLDecoder streaming individual responses - """ - ... - -async def results( - self, - message_batch_id: str, - **kwargs -) -> AsyncJSONLDecoder[BetaMessageBatchIndividualResponse]: - """Async version of results.""" - ... -``` - -## Examples - -### Basic Batch with Beta Features - -```python -batch = client.beta.messages.batches.create( - requests=[ - { - "custom_id": "request-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "thinking": {"type": "enabled"}, - "messages": [ - {"role": "user", "content": "Solve this problem..."} - ], - }, - }, - { - "custom_id": "request-2", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "web_search": {"type": "enabled"}, - "messages": [ - {"role": "user", "content": "What are the latest news?"} - ], - }, - } - ] -) - -print(f"Batch ID: {batch.id}") -``` - -### Wait for Completion and Get Results - -```python -import time - -# Wait for completion -while True: - batch = client.beta.messages.batches.retrieve(batch.id) - if batch.processing_status == "ended": - break - print(f"Processing: {batch.request_counts.processing} requests remaining") - time.sleep(60) - -# Process results -for response in client.beta.messages.batches.results(batch.id): - if response.result.type == "succeeded": - print(f"{response.custom_id}: Success") - # Access beta feature content - for block in response.result.message.content: - if block.type == "thinking": - print(f" Reasoning: {block.thinking[:100]}...") - elif block.type == "text": - print(f" Response: {block.text[:100]}...") - elif response.result.type == "errored": - print(f"{response.custom_id}: Error - {response.result.error.message}") -``` - -### Mixed Beta Features in Batch - -```python -# Create batch with different features per request -requests = [ - { - "custom_id": "thinking-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 4096, - "thinking": {"type": "enabled", "budget_tokens": 2000}, - "messages": [{"role": "user", "content": "Complex reasoning task"}] - } - }, - { - "custom_id": "citations-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "citations": {"type": "enabled"}, - "messages": [{ - "role": "user", - "content": [ - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, - {"type": "text", "text": "Summarize with citations"} - ] - }] - } - }, - { - "custom_id": "web-search-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "web_search": {"type": "enabled"}, - "messages": [{"role": "user", "content": "Latest AI news"}] - } - }, - { - "custom_id": "combined-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 4096, - "thinking": {"type": "enabled"}, - "web_search": {"type": "enabled"}, - "code_execution": {"type": "enabled"}, - "messages": [{"role": "user", "content": "Research and code solution"}] - } - } -] - -batch = client.beta.messages.batches.create(requests=requests) -``` - -### Large-Scale Beta Batch - -```python -# Process thousands of requests with beta features -requests = [] -for i in range(5000): - requests.append({ - "custom_id": f"request-{i}", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "thinking": {"type": "enabled"}, - "messages": [{"role": "user", "content": f"Task {i}: ..."}] - } - }) - -batch = client.beta.messages.batches.create(requests=requests) -print(f"Created batch {batch.id} with {len(requests)} requests") - -# Monitor progress -while True: - batch = client.beta.messages.batches.retrieve(batch.id) - completed = batch.request_counts.succeeded + batch.request_counts.errored - total = completed + batch.request_counts.processing - progress = (completed / total * 100) if total > 0 else 0 - - print(f"Progress: {progress:.1f}% ({completed}/{total})") - - if batch.processing_status == "ended": - break - time.sleep(60) - -# Process all results -results_by_id = {} -for response in client.beta.messages.batches.results(batch.id): - results_by_id[response.custom_id] = response.result - -print(f"Processed {len(results_by_id)} results") -``` - -### Async Batch Operations - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - # Create batch - batch = await client.beta.messages.batches.create( - requests=[ - { - "custom_id": "async-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "thinking": {"type": "enabled"}, - "messages": [{"role": "user", "content": "Task 1"}] - } - }, - { - "custom_id": "async-2", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "web_search": {"type": "enabled"}, - "messages": [{"role": "user", "content": "Task 2"}] - } - } - ] - ) - - # Poll until complete - while True: - batch = await client.beta.messages.batches.retrieve(batch.id) - if batch.processing_status == "ended": - break - await asyncio.sleep(60) - - # Get results - results = [] - async for response in client.beta.messages.batches.results(batch.id): - results.append(response) - - return results - -results = asyncio.run(main()) -``` - -### Cancel Batch - -```python -# Cancel batch in progress -batch = client.beta.messages.batches.cancel("batch_abc123") -print(f"Status: {batch.processing_status}") # "canceling" - -# Wait for cancellation to complete -while True: - batch = client.beta.messages.batches.retrieve(batch.id) - if batch.processing_status == "ended": - break - time.sleep(30) - -print(f"Canceled: {batch.request_counts.canceled} requests") -``` - -### Delete Batch - -```python -# Delete completed batch -deleted = client.beta.messages.batches.delete("batch_abc123") -print(f"Deleted batch: {deleted.id}") -``` - -### List All Batches - -```python -# List recent batches -for batch in client.beta.messages.batches.list(limit=10): - print(f"{batch.id}: {batch.processing_status}") - print(f" Succeeded: {batch.request_counts.succeeded}") - print(f" Errored: {batch.request_counts.errored}") - print(f" Processing: {batch.request_counts.processing}") -``` - -### Error Handling - -```python -from anthropic import APIError - -try: - batch = client.beta.messages.batches.create(requests=[...]) -except APIError as e: - print(f"Failed to create batch: {e.message}") - -# Process results with error handling -for response in client.beta.messages.batches.results(batch.id): - if response.result.type == "succeeded": - try: - # Process successful result - message = response.result.message - ... - except Exception as e: - print(f"Error processing {response.custom_id}: {e}") - elif response.result.type == "errored": - print(f"Request {response.custom_id} failed: {response.result.error.message}") - elif response.result.type == "canceled": - print(f"Request {response.custom_id} was canceled") - elif response.result.type == "expired": - print(f"Request {response.custom_id} expired") -``` - -## Best Practices - -### 1. Use Meaningful Custom IDs - -```python -# Good - descriptive IDs -custom_id = f"user-{user_id}-task-{task_id}-{timestamp}" - -# Bad - generic IDs -custom_id = f"request-{i}" -``` - -### 2. Batch Similar Requests - -Group requests with similar beta features: - -```python -# Batch 1: Thinking-heavy tasks -thinking_requests = [...] - -# Batch 2: Web search tasks -search_requests = [...] - -batch1 = client.beta.messages.batches.create(requests=thinking_requests) -batch2 = client.beta.messages.batches.create(requests=search_requests) -``` - -### 3. Monitor Progress - -```python -def monitor_batch(batch_id): - """Monitor batch with progress updates.""" - last_progress = 0 - - while True: - batch = client.beta.messages.batches.retrieve(batch_id) - - total = sum([ - batch.request_counts.processing, - batch.request_counts.succeeded, - batch.request_counts.errored, - batch.request_counts.canceled - ]) - completed = batch.request_counts.succeeded + batch.request_counts.errored - progress = (completed / total * 100) if total > 0 else 0 - - if progress > last_progress + 5: # Log every 5% - print(f"Progress: {progress:.1f}%") - last_progress = progress - - if batch.processing_status == "ended": - break - - time.sleep(60) - - return batch - -batch = monitor_batch("batch_abc123") -``` - -### 4. Handle All Result Types - -```python -success_count = 0 -error_count = 0 -canceled_count = 0 -expired_count = 0 - -for response in client.beta.messages.batches.results(batch.id): - if response.result.type == "succeeded": - success_count += 1 - process_success(response) - elif response.result.type == "errored": - error_count += 1 - log_error(response) - elif response.result.type == "canceled": - canceled_count += 1 - elif response.result.type == "expired": - expired_count += 1 - -print(f"Results: {success_count} success, {error_count} errors, {canceled_count} canceled, {expired_count} expired") -``` - -### 5. Batch Size Considerations - -- Maximum 10,000 requests per batch -- Consider splitting very large workloads -- Balance batch size with monitoring needs - -```python -def create_batches_chunked(requests, chunk_size=5000): - """Split large request list into multiple batches.""" - batches = [] - - for i in range(0, len(requests), chunk_size): - chunk = requests[i:i+chunk_size] - batch = client.beta.messages.batches.create(requests=chunk) - batches.append(batch) - - return batches - -all_requests = [...] # 20,000 requests -batches = create_batches_chunked(all_requests) -``` - -## Limitations and Considerations - -### Beta Feature Support -- All beta features supported in batches -- Same limitations apply as in standard requests -- Token budgets apply per request - -### Processing Time -- Batches typically complete within 24 hours -- Large batches may take longer -- Complexity affects processing time - -### Cost Optimization -- 50% cost reduction vs standard API -- Beta features may have additional costs -- Calculate total cost including beta feature usage - -### Rate Limits -- Batch requests don't count against rate limits -- Batch creation has its own limits -- Monitor batch creation rate - -## See Also - -- [Beta Overview](./index.md) - Overview of all beta features -- [Message Features](./message-features.md) - Beta feature documentation -- [Standard Batches](../api/batches.md) - Standard batch processing -- [Messages API](../api/messages.md) - Core message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md deleted file mode 100644 index 0dc9a3300..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/files.md +++ /dev/null @@ -1,669 +0,0 @@ -# Beta Files API - -Upload and manage files for use in conversations and batch processing. - -## Overview - -The Files API enables you to upload, manage, and download files for use with Claude. Files can be used for document analysis, batch processing input, or general user data. - -## Key Features - -- Upload files with specified purpose (batch or user_data) -- Retrieve file metadata -- List uploaded files with pagination -- Download file content -- Delete files when no longer needed - -## Files API - -### Upload File - -```python { .api } -def upload( - self, - *, - file: FileTypes, - purpose: Literal["batch", "user_data"], - **kwargs -) -> FileMetadata: - """ - Upload file. - - Parameters: - file: File to upload (bytes, file path, or file object) - purpose: Purpose of file - "batch" - For batch processing requests/results - "user_data" - For general document analysis and user data - - Returns: - FileMetadata with file ID, size, created timestamp, and purpose - """ - ... -``` - -### Retrieve File Metadata - -```python { .api } -def retrieve( - self, - file_id: str, - **kwargs -) -> FileMetadata: - """ - Get file metadata. - - Parameters: - file_id: Unique identifier for the file - - Returns: - FileMetadata with file details (ID, filename, size, purpose, created_at) - """ - ... -``` - -### List Files - -```python { .api } -def list( - self, - *, - before_id: str = NOT_GIVEN, - after_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - **kwargs -) -> SyncPage[FileMetadata]: - """ - List uploaded files with pagination. - - Parameters: - before_id: Return files before this ID (for reverse pagination) - after_id: Return files after this ID (for forward pagination) - limit: Maximum number of files to return (default varies by API) - - Returns: - SyncPage[FileMetadata] with paginated file list - """ - ... -``` - -### Delete File - -```python { .api } -def delete( - self, - file_id: str, - **kwargs -) -> DeletedFile: - """ - Delete a file. - - Parameters: - file_id: Unique identifier for the file to delete - - Returns: - DeletedFile confirming deletion - """ - ... -``` - -### Download File - -```python { .api } -def download( - self, - file_id: str, - **kwargs -) -> bytes: - """ - Download file content. - - Parameters: - file_id: Unique identifier for the file - - Returns: - bytes: Raw file content - """ - ... -``` - -## Examples - -### Upload File from Path - -```python -from anthropic import Anthropic -from anthropic._utils import file_from_path - -client = Anthropic() - -# Upload PDF for document analysis -file = file_from_path("document.pdf") -uploaded = client.beta.files.upload( - file=file, - purpose="user_data" -) - -print(f"Uploaded: {uploaded.id}") -print(f"Filename: {uploaded.filename}") -print(f"Size: {uploaded.size} bytes") -print(f"Created: {uploaded.created_at}") -``` - -### Upload File from Bytes - -```python -# Upload file from bytes -file_content = b"Sample document content..." -uploaded = client.beta.files.upload( - file=("document.txt", file_content), - purpose="user_data" -) - -print(f"Uploaded file ID: {uploaded.id}") -``` - -### Upload for Batch Processing - -```python -import json - -# Prepare batch requests -batch_requests = [ - { - "custom_id": "request-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": "Hello"}] - } - } -] - -# Convert to JSONL format -jsonl_content = "\n".join(json.dumps(req) for req in batch_requests) - -# Upload batch file -batch_file = client.beta.files.upload( - file=("batch_requests.jsonl", jsonl_content.encode()), - purpose="batch" -) - -print(f"Batch file uploaded: {batch_file.id}") -``` - -### Retrieve File Metadata - -```python -# Get file details -file_metadata = client.beta.files.retrieve("file_abc123") - -print(f"ID: {file_metadata.id}") -print(f"Filename: {file_metadata.filename}") -print(f"Size: {file_metadata.size} bytes") -print(f"Purpose: {file_metadata.purpose}") -print(f"Created: {file_metadata.created_at}") -``` - -### List All Files - -```python -# List all uploaded files -for file in client.beta.files.list(): - print(f"{file.filename} ({file.id}) - {file.purpose}") -``` - -### List Files with Pagination - -```python -# List files with limit -page1 = client.beta.files.list(limit=10) -for file in page1: - print(f"File: {file.filename}") - -# Get next page -if page1.has_next_page(): - page2 = client.beta.files.list(limit=10, after_id=page1.data[-1].id) - for file in page2: - print(f"File: {file.filename}") - -# List files in reverse -recent_files = client.beta.files.list(limit=5) -older_files = client.beta.files.list( - limit=5, - before_id=recent_files.data[0].id -) -``` - -### Download File - -```python -# Download file content -file_id = "file_abc123" -content = client.beta.files.download(file_id) - -# Save to disk -with open("downloaded_file.pdf", "wb") as f: - f.write(content) - -print(f"Downloaded {len(content)} bytes") -``` - -### Delete File - -```python -# Delete file -deleted = client.beta.files.delete("file_abc123") -print(f"Deleted file: {deleted.id}") -print(f"Deleted: {deleted.deleted}") -``` - -### Use File in Message - -```python -# Upload document -file = file_from_path("research_paper.pdf") -uploaded = client.beta.files.upload( - file=file, - purpose="user_data" -) - -# Use file in message with citations -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - citations={"type": "enabled"}, - messages=[ - { - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": uploaded.id # Use file ID - } - }, - { - "type": "text", - "text": "Summarize this document with citations" - } - ] - } - ] -) - -# Process citations -for block in message.content: - if hasattr(block, 'citations'): - for citation in block.citations: - print(f"Citation: {citation.cited_text}") -``` - -### Manage Multiple Files - -```python -from anthropic._utils import file_from_path - -# Upload multiple files -files_to_upload = [ - "document1.pdf", - "document2.pdf", - "data.csv" -] - -uploaded_files = [] -for filepath in files_to_upload: - file = file_from_path(filepath) - uploaded = client.beta.files.upload( - file=file, - purpose="user_data" - ) - uploaded_files.append(uploaded) - print(f"Uploaded {uploaded.filename}: {uploaded.id}") - -# Track uploaded files -file_ids = [f.id for f in uploaded_files] -print(f"Uploaded {len(file_ids)} files: {file_ids}") - -# Later: clean up files -for file_id in file_ids: - client.beta.files.delete(file_id) - print(f"Deleted {file_id}") -``` - -### Async File Operations - -```python -import asyncio -from anthropic import AsyncAnthropic -from anthropic._utils import file_from_path - -async def main(): - client = AsyncAnthropic() - - # Upload file - file = file_from_path("document.pdf") - uploaded = await client.beta.files.upload( - file=file, - purpose="user_data" - ) - print(f"Uploaded: {uploaded.id}") - - # List files - files = await client.beta.files.list(limit=10) - async for file in files: - print(f"File: {file.filename}") - - # Download file - content = await client.beta.files.download(uploaded.id) - print(f"Downloaded {len(content)} bytes") - - # Delete file - deleted = await client.beta.files.delete(uploaded.id) - print(f"Deleted: {deleted.id}") - -asyncio.run(main()) -``` - -### Error Handling - -```python -from anthropic import APIError, NotFoundError, BadRequestError - -# Upload with error handling -try: - file = file_from_path("large_document.pdf") - uploaded = client.beta.files.upload( - file=file, - purpose="user_data" - ) - print(f"Uploaded: {uploaded.id}") -except BadRequestError as e: - if "size" in str(e).lower(): - print(f"File too large: {e.message}") - else: - print(f"Invalid file: {e.message}") -except APIError as e: - print(f"Upload failed: {e.message}") - -# Download with error handling -try: - content = client.beta.files.download("file_abc123") - with open("output.pdf", "wb") as f: - f.write(content) -except NotFoundError: - print("File not found") -except APIError as e: - print(f"Download failed: {e.message}") - -# Delete with validation -file_id = "file_abc123" -try: - # Check if file exists - file_metadata = client.beta.files.retrieve(file_id) - print(f"Deleting {file_metadata.filename}") - - # Delete file - deleted = client.beta.files.delete(file_id) - print(f"Deleted: {deleted.deleted}") -except NotFoundError: - print(f"File {file_id} not found") -except APIError as e: - print(f"Deletion failed: {e.message}") -``` - -### File Lifecycle Management - -```python -class FileManager: - """Manage file uploads with automatic cleanup.""" - - def __init__(self, client): - self.client = client - self.uploaded_files = [] - - def upload(self, filepath, purpose="user_data"): - """Upload file and track it.""" - file = file_from_path(filepath) - uploaded = self.client.beta.files.upload( - file=file, - purpose=purpose - ) - self.uploaded_files.append(uploaded.id) - return uploaded - - def cleanup(self): - """Delete all tracked files.""" - for file_id in self.uploaded_files: - try: - self.client.beta.files.delete(file_id) - print(f"Deleted {file_id}") - except APIError as e: - print(f"Failed to delete {file_id}: {e.message}") - self.uploaded_files.clear() - -# Usage -manager = FileManager(client) - -try: - # Upload files - file1 = manager.upload("doc1.pdf") - file2 = manager.upload("doc2.pdf") - - # Use files... - print(f"Using files: {file1.id}, {file2.id}") - -finally: - # Always cleanup - manager.cleanup() -``` - -### Check File Before Use - -```python -def safe_file_operation(file_id): - """Safely perform operations on a file.""" - try: - # Verify file exists - metadata = client.beta.files.retrieve(file_id) - - # Check file properties - if metadata.purpose != "user_data": - print(f"Warning: File has purpose '{metadata.purpose}'") - - # Download if needed - content = client.beta.files.download(file_id) - print(f"File size: {len(content)} bytes") - - return content - - except NotFoundError: - print(f"File {file_id} not found") - return None - except APIError as e: - print(f"Error: {e.message}") - return None - -# Use safely -content = safe_file_operation("file_abc123") -if content: - # Process content - pass -``` - -## File Purposes - -### batch - -Used for batch processing operations: -- Batch request files (JSONL format) -- Batch result files -- Structured data for bulk operations - -Example: -```python -batch_file = client.beta.files.upload( - file=("requests.jsonl", jsonl_data), - purpose="batch" -) -``` - -### user_data - -Used for general document analysis and conversation: -- PDF documents -- Text files -- Images -- CSV data -- Any user-provided content - -Example: -```python -doc_file = client.beta.files.upload( - file=file_from_path("document.pdf"), - purpose="user_data" -) -``` - -## Best Practices - -### 1. Use Appropriate Purpose - -Choose the right purpose for your use case: -```python -# For batch processing -client.beta.files.upload(file=batch_data, purpose="batch") - -# For document analysis -client.beta.files.upload(file=document, purpose="user_data") -``` - -### 2. Track Uploaded Files - -Keep track of file IDs for cleanup: -```python -uploaded_ids = [] -for doc in documents: - uploaded = client.beta.files.upload(file=doc, purpose="user_data") - uploaded_ids.append(uploaded.id) - -# Later cleanup -for file_id in uploaded_ids: - client.beta.files.delete(file_id) -``` - -### 3. Handle Errors Gracefully - -Always handle file operation errors: -```python -try: - uploaded = client.beta.files.upload(file=large_file, purpose="user_data") -except BadRequestError as e: - # Handle validation errors (size, format, etc.) - handle_validation_error(e) -except APIError as e: - # Handle other API errors - handle_api_error(e) -``` - -### 4. Clean Up Unused Files - -Delete files when no longer needed: -```python -# After processing -client.beta.files.delete(file_id) -``` - -### 5. Use file_from_path Helper - -Leverage the SDK helper for file uploads: -```python -from anthropic._utils import file_from_path - -# Automatically handles file reading and metadata -file = file_from_path("document.pdf") -uploaded = client.beta.files.upload(file=file, purpose="user_data") -``` - -### 6. Validate Before Upload - -Check file properties before uploading: -```python -import os - -def validate_and_upload(filepath, max_size_mb=10): - """Validate file before upload.""" - if not os.path.exists(filepath): - raise ValueError(f"File not found: {filepath}") - - size_mb = os.path.getsize(filepath) / (1024 * 1024) - if size_mb > max_size_mb: - raise ValueError(f"File too large: {size_mb:.1f}MB (max {max_size_mb}MB)") - - return client.beta.files.upload( - file=file_from_path(filepath), - purpose="user_data" - ) -``` - -### 7. Pagination for Large Lists - -Use pagination for many files: -```python -def list_all_files(client): - """List all files with pagination.""" - all_files = [] - after_id = None - - while True: - page = client.beta.files.list(limit=100, after_id=after_id) - all_files.extend(page.data) - - if not page.has_next_page(): - break - - after_id = page.data[-1].id - - return all_files -``` - -## Limitations and Considerations - -### File Size Limits - -- Check API documentation for current file size limits -- Limits may vary by purpose (batch vs user_data) -- Consider splitting large files if possible - -### Supported Formats - -- PDFs (for document analysis) -- Text files (plain text, markdown, etc.) -- Images (PNG, JPEG, etc.) -- JSONL (for batch processing) -- CSV and other structured data formats - -### File Retention - -- Files persist until explicitly deleted -- No automatic cleanup -- Monitor storage usage - -### Purpose-Specific Constraints - -- batch: Must be valid JSONL format for batch requests -- user_data: General content, validated by API - -### Rate Limits - -- File upload operations count against rate limits -- Large files may take longer to process -- Consider rate limiting for bulk uploads - -## See Also - -- [Beta Overview](./index.md) - Overview of all beta features -- [Skills API](./skills.md) - Create and manage reusable skills -- [Beta Batches](./batches.md) - Batch processing with files -- [Message Features](./message-features.md) - Using files in messages with citations diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md deleted file mode 100644 index 907b45c2c..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/index.md +++ /dev/null @@ -1,358 +0,0 @@ -# Beta Features Overview - -Access experimental capabilities through the `client.beta` namespace including extended thinking, citations, web search, code execution, computer use, and more. - -## What Are Beta Features? - -Beta features provide experimental functionality that may change in future releases. They enable advanced capabilities beyond standard message creation. - -## Available Beta Features - -### Message Enhancement Features - -Add advanced capabilities to message creation: - -- **[Extended Thinking](./message-features.md#extended-thinking)** - Enable Claude to show detailed reasoning with configurable token budget -- **[Citations](./message-features.md#citations)** - Source attribution for responses when working with documents -- **[Web Search](./message-features.md#web-search)** - Real-time web information retrieval -- **[Code Execution](./message-features.md#code-execution)** - Python code execution in secure sandbox -- **[Computer Use](./message-features.md#computer-use)** - GUI interaction capabilities (screenshots, mouse, keyboard) -- **[Bash Commands](./message-features.md#bash-commands)** - Shell command execution -- **[Text Editor](./message-features.md#text-editor)** - Text file editing capabilities -- **[MCP Integration](./message-features.md#mcp-integration)** - Model Context Protocol tool integration -- **[Memory Tools](./message-features.md#memory-tools)** - Persistent memory across conversations - -**[→ Message Features Documentation](./message-features.md)** - -### Resource APIs - -Manage resources and process messages at scale: - -- **[Skills API](./skills.md)** - Create and manage reusable capabilities with version control -- **[Files API](./files.md)** - Upload and manage files for use in conversations -- **[Beta Message Batches](./batches.md)** - Process multiple beta messages asynchronously with all features - -## Quick Start - -### Enable Extended Thinking - -```python -from anthropic import Anthropic - -client = Anthropic() - -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=4096, - thinking={"type": "enabled", "budget_tokens": 2000}, - messages=[ - {"role": "user", "content": "Solve this complex problem: ..."} - ] -) - -# Access thinking and response -for block in message.content: - if block.type == "thinking": - print(f"Reasoning: {block.thinking}") - elif block.type == "text": - print(f"Answer: {block.text}") -``` - -### Combine Multiple Features - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=4096, - thinking={"type": "enabled"}, - web_search={"type": "enabled"}, - citations={"type": "enabled"}, - messages=[ - { - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": pdf_data - } - }, - {"type": "text", "text": "Analyze this paper and find related research"} - ] - } - ] -) -``` - -## Beta Messages API - -The beta messages API extends the standard messages API with additional parameters. - -```python { .api } -def create( - self, - *, - model: str, - messages: list[BetaMessageParam], - max_tokens: int, - # Standard parameters - system: str | list[BetaTextBlockParam] = NOT_GIVEN, - metadata: MetadataParam = NOT_GIVEN, - stop_sequences: list[str] = NOT_GIVEN, - stream: bool = False, - temperature: float = NOT_GIVEN, - top_p: float = NOT_GIVEN, - top_k: int = NOT_GIVEN, - tools: list[BetaToolParam] = NOT_GIVEN, - tool_choice: BetaToolChoice = NOT_GIVEN, - # Beta feature parameters - thinking: ThinkingConfigParam = NOT_GIVEN, - citations: CitationsConfigParam = NOT_GIVEN, - web_search: WebSearchConfigParam = NOT_GIVEN, - code_execution: CodeExecutionConfigParam = NOT_GIVEN, - bash: BashConfigParam = NOT_GIVEN, - text_editor: TextEditorConfigParam = NOT_GIVEN, - computer_use: ComputerUseConfigParam = NOT_GIVEN, - mcp: MCPConfigParam = NOT_GIVEN, - memory: MemoryConfigParam = NOT_GIVEN, - context: ContextConfigParam = NOT_GIVEN, - **kwargs -) -> BetaMessage: - """ - Create message with beta features. - - All standard message parameters are supported, plus beta feature configurations. - Multiple beta features can be enabled simultaneously. - - Returns: - BetaMessage with beta content blocks - """ - ... - -async def create(self, **kwargs) -> BetaMessage: - """Async version of create.""" - ... -``` - -### Streaming - -```python { .api } -def stream( - self, - **kwargs -) -> BetaMessageStreamManager: - """Stream message with beta features.""" - ... - -def stream(self, **kwargs) -> BetaAsyncMessageStreamManager: - """Async version of stream.""" - ... -``` - -### Count Tokens - -```python { .api } -def count_tokens( - self, - *, - model: str, - messages: list[BetaMessageParam], - system: str | list[BetaTextBlockParam] = NOT_GIVEN, - tools: list[BetaToolParam] = NOT_GIVEN, - tool_choice: BetaToolChoice = NOT_GIVEN, - **kwargs -) -> BetaMessageTokensCount: - """Count tokens for beta message.""" - ... -``` - -### Tool Runner - -```python { .api } -def tool_runner( - self, - *, - model: str, - messages: list[BetaMessageParam], - max_tokens: int, - tools: list[BetaToolParam | BetaFunctionTool], - **kwargs -) -> Iterator[BetaMessage]: - """Automatically execute tools in beta messages.""" - ... -``` - -## Beta Models API - -Retrieve model information with beta feature support. - -```python { .api } -def retrieve( - self, - model_id: str, - **kwargs -) -> BetaModelInfo: - """Get information about a specific model.""" - ... - -def list( - self, - *, - before_id: str = NOT_GIVEN, - after_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - **kwargs -) -> SyncPage[BetaModelInfo]: - """List available models with pagination.""" - ... -``` - -## Architecture - -Beta features are organized into three categories: - -### 1. Message Enhancement Features -Parameters that enhance `client.beta.messages.create()`: -- Enable advanced reasoning (thinking) -- Add real-time information (web search) -- Enable code execution -- Support document citations -- Enable computer interaction - -**[→ Full Documentation](./message-features.md)** - -### 2. Batch Processing -Process multiple beta messages with all features: -- Create batches with thinking, citations, web search, etc. -- Same interface as standard batches -- 50% cost reduction - -**[→ Batches Documentation](./batches.md)** - -### 3. Resource Management -Manage files and skills: -- Upload files for document analysis -- Create reusable skills -- Version control for skills - -**[→ Skills Documentation](./skills.md)** | **[→ Files Documentation](./files.md)** - -## Feature Comparison - -| Feature | Standard API | Beta API | -|---------|-------------|----------| -| Basic messaging | ✓ | ✓ | -| Streaming | ✓ | ✓ | -| Tool use | ✓ | ✓ | -| Extended thinking | ✗ | ✓ | -| Citations | ✗ | ✓ | -| Web search | ✗ | ✓ | -| Code execution | ✗ | ✓ | -| Computer use | ✗ | ✓ | -| Batches | ✓ | ✓ (with beta features) | -| Skills management | ✗ | ✓ | -| File management | ✗ | ✓ | - -## Important Notes - -### Stability and Changes -- **Beta features may change without notice** -- APIs may be modified or removed -- Breaking changes possible between releases -- Not recommended for production-critical features - -### Availability -- Not all features available in all regions -- Some features require specific model versions -- Check current documentation for availability - -### Usage and Costs -- Rate limits may differ for beta features -- Beta features may have additional usage costs -- Token budgets (e.g., thinking) are approximate -- Web search adds latency to requests - -### Technical Limitations -- Citations work best with document inputs (PDFs, text files) -- Code execution runs in isolated sandbox with limited packages -- Computer use requires display configuration and has security restrictions -- Web search requires internet connectivity -- Skills must include a SKILL.md file at the root -- File uploads have size limits (check API documentation) - -### Best Practices -- Test beta features in development before deploying -- Handle feature deprecation gracefully -- Monitor for API changes and updates -- Use appropriate error handling for experimental features -- Check feature availability before use - -## Migration from Standard API - -Most code using the standard API can be upgraded to beta with minimal changes: - -```python -# Standard API -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) - -# Beta API (backward compatible) -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) - -# Beta API (with features) -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - thinking={"type": "enabled"}, # Add beta features - messages=[{"role": "user", "content": "Hello"}] -) -``` - -## Documentation Structure - -### Message Enhancement Features -**[message-features.md](./message-features.md)** - Comprehensive guide to all message enhancement parameters: -- Extended thinking with budget control -- Citations configuration and usage -- Web search integration -- Code execution in sandbox -- Computer use capabilities -- Bash, text editor, MCP, memory tools -- Combined features examples -- Streaming with beta features - -### Resource APIs -**[batches.md](./batches.md)** - Beta message batches: -- Create batches with beta features -- Retrieve, list, cancel, delete operations -- Process results with JSONL decoder -- All beta features supported in batches - -**[skills.md](./skills.md)** - Skills management: -- Create and manage reusable skills -- Version control for skills -- List, retrieve, delete operations -- Skill file requirements - -**[files.md](./files.md)** - File management: -- Upload files for document analysis -- Download and list files -- Delete file operations -- File size limits and formats - -## See Also - -- [Messages API](../api/messages.md) - Core message creation -- [Tool Use API](../api/tools.md) - Tool integration -- [Streaming API](../api/streaming.md) - Streaming responses -- [Batches API](../api/batches.md) - Standard batch processing -- [Getting Started](../guides/getting-started.md) - Basic SDK usage diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md deleted file mode 100644 index be7157a54..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/message-features.md +++ /dev/null @@ -1,1134 +0,0 @@ -# Beta Message Enhancement Features - -Message enhancement features are parameters that add advanced capabilities to `client.beta.messages.create()`. Multiple features can be combined in a single request. - -## Extended Thinking - -Enable Claude to show detailed reasoning process with configurable token budget. - -### API - -```python { .api } -class ThinkingConfigParam(TypedDict): - """ - Extended thinking configuration. - - Fields: - type: "enabled" or "disabled" - budget_tokens: Maximum tokens for thinking (optional) - """ - type: Literal["enabled", "disabled"] - budget_tokens: NotRequired[int] - -class ThinkingBlock(BaseModel): - """ - Thinking content block in response. - - Attributes: - type: Always "thinking" - thinking: The reasoning content - """ - type: Literal["thinking"] - thinking: str - -class RedactedThinkingBlock(BaseModel): - """ - Redacted thinking block (when thinking disabled mid-conversation). - - Attributes: - type: Always "redacted_thinking" - """ - type: Literal["redacted_thinking"] -``` - -### Example - -```python -from anthropic import Anthropic - -client = Anthropic() - -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=4096, - thinking={ - "type": "enabled", - "budget_tokens": 2000, - }, - messages=[ - { - "role": "user", - "content": "Solve this complex math problem: ..." - } - ] -) - -# Check for thinking blocks -for block in message.content: - if block.type == "thinking": - print("Claude's reasoning:") - print(block.thinking) - elif block.type == "text": - print("\nFinal answer:") - print(block.text) -``` - -### Notes - -- Token budgets are approximate and may be exceeded slightly -- Thinking blocks appear before text blocks in the response -- Disabling thinking mid-conversation results in redacted blocks -- Works with all model versions that support beta features - -## Structured Outputs - -Enable type-safe, validated responses by specifying a Pydantic model schema. Claude will generate output that conforms to your schema and the SDK will automatically parse and validate it. - -### API - -```python { .api } -from typing import Generic, TypeVar, Optional -from pydantic import BaseModel - -ResponseFormatT = TypeVar('ResponseFormatT', bound=BaseModel) - -class ParsedBetaMessage(BetaMessage, Generic[ResponseFormatT]): - """ - Beta message response with parsed structured output. - - Attributes: - content: List of content blocks including ParsedBetaTextBlock - parsed_output: Convenience property to access the parsed Pydantic model - """ - content: list[ParsedBetaContentBlock[ResponseFormatT]] - - @property - def parsed_output(self) -> Optional[ResponseFormatT]: - """Extract the first parsed output from text content blocks.""" - ... - -class ParsedBetaTextBlock(BetaTextBlock, Generic[ResponseFormatT]): - """ - Text content block with parsed structured output. - - Attributes: - type: Always "text" - text: Raw JSON text - parsed_output: Validated Pydantic model instance - """ - type: Literal["text"] - text: str - parsed_output: Optional[ResponseFormatT] - -def transform_schema( - json_schema: type[BaseModel] | dict[str, Any], -) -> dict[str, Any]: - """ - Transform a Pydantic model or JSON schema for API compatibility. - - Handles: - - Format conversion for supported types - - Property transformations - - Unsupported property documentation - - Args: - json_schema: Pydantic BaseModel class or dict schema - - Returns: - Transformed schema dict compatible with API - """ - ... -``` - -### Basic Example - -```python -import pydantic -from anthropic import Anthropic - -# Define your output schema -class Order(pydantic.BaseModel): - product_name: str - price: float - quantity: int - -client = Anthropic() - -prompt = """ -Extract the product name, price, and quantity from this customer message: -"Hi, I'd like to order 2 packs of Green Tea for 5.50 dollars each." -""" - -# Use parse() method with output_format parameter -parsed_message = client.beta.messages.parse( - model="claude-sonnet-4-5-20250929", - messages=[{"role": "user", "content": prompt}], - max_tokens=1024, - output_format=Order, -) - -# Access parsed output directly -order = parsed_message.parsed_output -print(f"Product: {order.product_name}") -print(f"Price: ${order.price}") -print(f"Quantity: {order.quantity}") -# Output: -# Product: Green Tea -# Price: $5.5 -# Quantity: 2 -``` - -### Streaming Example - -```python -# Stream with structured outputs -with client.beta.messages.stream( - model="claude-sonnet-4-5-20250929", - messages=[{"role": "user", "content": prompt}], - max_tokens=1024, - output_format=Order, -) as stream: - for event in stream: - if event.type == "text": - # Get partial parsed output as it streams - partial = event.parsed_snapshot() - print(f"Partial: {partial}") - -# Get final parsed result -final_message = stream.get_final_message() -order = final_message.parsed_output -``` - -### Complex Schema Example - -```python -from typing import Literal -import pydantic - -class Address(pydantic.BaseModel): - street: str - city: str - postal_code: str - country: str - -class Customer(pydantic.BaseModel): - name: str - email: str - phone: str | None = None - address: Address - -class OrderItem(pydantic.BaseModel): - product_id: str - product_name: str - quantity: int - price: float - -class CompleteOrder(pydantic.BaseModel): - order_id: str - customer: Customer - items: list[OrderItem] - total_amount: float - status: Literal["pending", "confirmed", "shipped", "delivered"] - notes: str | None = None - -# Use with complex nested schema -parsed_message = client.beta.messages.parse( - model="claude-sonnet-4-5-20250929", - messages=[{ - "role": "user", - "content": "Extract order details from: [long email or document]" - }], - max_tokens=2048, - output_format=CompleteOrder, -) - -order = parsed_message.parsed_output -print(f"Order ID: {order.order_id}") -print(f"Customer: {order.customer.name}") -print(f"Items: {len(order.items)}") -for item in order.items: - print(f" - {item.product_name}: ${item.price} x {item.quantity}") -print(f"Total: ${order.total_amount}") -``` - -### Using transform_schema() - -```python -from anthropic import transform_schema - -# Transform a Pydantic model -class MyModel(pydantic.BaseModel): - name: str - age: int = pydantic.Field(ge=0, le=150, description="Age in years") - -# Get transformed schema -schema = transform_schema(MyModel) -print(schema) -# Transforms Pydantic schema to API-compatible format -# Unsupported constraints (ge, le) are moved to description - -# Or transform a dict schema directly -schema = transform_schema({ - "type": "integer", - "minimum": 1, - "maximum": 10, - "description": "A number" -}) -# Returns: {'type': 'integer', 'description': 'A number\n\n{minimum: 1, maximum: 10}'} -``` - -### Async Example - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - parsed_message = await client.beta.messages.parse( - model="claude-sonnet-4-5-20250929", - messages=[{"role": "user", "content": prompt}], - max_tokens=1024, - output_format=Order, - ) - - order = parsed_message.parsed_output - print(f"Extracted: {order}") - -asyncio.run(main()) -``` - -### Async Streaming Example - -```python -async def main(): - client = AsyncAnthropic() - - async with client.beta.messages.stream( - model="claude-sonnet-4-5-20250929", - messages=[{"role": "user", "content": prompt}], - max_tokens=1024, - output_format=Order, - ) as stream: - async for event in stream: - if event.type == "text": - partial = event.parsed_snapshot() - print(f"Partial: {partial}") - - final_message = await stream.get_final_message() - order = final_message.parsed_output - -asyncio.run(main()) -``` - -### Notes - -- Requires models that support structured outputs (e.g., claude-sonnet-4-5-20250929) -- Uses Pydantic for schema definition and validation -- The `parse()` method is a convenience wrapper around `create()` that handles schema transformation -- Automatically validates output against your schema -- Raises `pydantic.ValidationError` if output doesn't match schema -- In streaming mode, `parsed_snapshot()` provides incremental parsing -- The feature automatically adds `"structured-outputs-2025-11-13"` beta header -- Supported formats: date-time, time, date, duration, email, hostname, uri, ipv4, ipv6, uuid -- Unsupported Pydantic constraints (ge, le, gt, lt, etc.) are documented in schema description - -## Context Management - -Automatically manage conversation context by clearing thinking blocks or tool uses when limits are approached. - -### API - -```python { .api } -class BetaContextManagementConfigParam(TypedDict, total=False): - edits: list[Union[BetaClearToolUses20250919EditParam, BetaClearThinking20251015EditParam]] - -class BetaClearThinking20251015EditParam(TypedDict, total=False): - type: Literal["clear_thinking_20251015"] - keep: Union[int, Literal["all"]] # Number of recent turns to keep thinking blocks - -class BetaClearToolUses20250919EditParam(TypedDict, total=False): - type: Literal["clear_tool_uses_20250919"] - trigger: Union[BetaInputTokensTriggerParam, BetaToolUsesTriggerParam] - keep: BetaToolUsesKeepParam - clear_at_least: Optional[BetaInputTokensClearAtLeastParam] - clear_tool_inputs: Union[bool, list[str], None] - exclude_tools: Optional[list[str]] -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - thinking={"type": "enabled"}, - context_management={ - "edits": [ - {"type": "clear_thinking_20251015", "keep": 3}, # Keep last 3 turns - { - "type": "clear_tool_uses_20250919", - "trigger": {"type": "input_tokens", "value": 100000}, - "keep": {"type": "turns", "value": 5}, - "clear_tool_inputs": True, - } - ] - }, - messages=conversation_history, -) -``` - -## Container Support - -Enable container-based file operations for beta features. - -### API - -```python { .api } -class BetaContainerParams(TypedDict, total=False): - # Container configuration parameters (check API docs for details) - pass - -class BetaContainerUploadBlock(BaseModel): - type: Literal["container_upload"] - # Upload result information -``` - -## Search Tools - -Enable document search capabilities with BM25 or regex patterns. - -### API - -```python { .api } -class BetaToolSearchToolBM2520251119Param(TypedDict, total=False): - """BM25 search tool for document retrieval.""" - type: Literal["search_tool_bm25_20251119"] - name: str - -class BetaToolSearchToolRegex20251119Param(TypedDict, total=False): - """Regex pattern search tool.""" - type: Literal["search_tool_regex_20251119"] - name: str -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[ - {"type": "search_tool_bm25_20251119", "name": "document_search"}, - ], - messages=[{"role": "user", "content": "Search for relevant passages about AI"}] -) -``` - -## Citations - -Enable source attribution for responses when working with documents. - -### API - -```python { .api } -class CitationsConfigParam(TypedDict): - """ - Citations configuration. - - Fields: - type: "enabled" or "disabled" - """ - type: Literal["enabled", "disabled"] - -class TextCitation(BaseModel): - """ - Citation in text content. - - Attributes: - type: Always "text_citation" - text: Cited text - cited_text: Original source text - location: Location in source - """ - type: Literal["text_citation"] - text: str - cited_text: str - location: CitationCharLocation | CitationContentBlockLocation | CitationPageLocation -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - citations={"type": "enabled"}, - messages=[ - { - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": pdf_data, - }, - }, - { - "type": "text", - "text": "Summarize this document with citations.", - }, - ], - } - ] -) - -# Extract citations -for block in message.content: - if hasattr(block, 'citations'): - for citation in block.citations: - print(f"Citation: {citation.cited_text}") - print(f"Location: {citation.location}") -``` - -### Notes - -- Works best with document inputs (PDFs, text files) -- Citations reference specific locations in source documents -- Multiple citations can appear in a single response -- Location types: character position, content block, or page number - -## Web Search - -Enable real-time web information retrieval. - -### API - -```python { .api } -class WebSearchConfigParam(TypedDict): - """ - Web search configuration. - - Fields: - type: "enabled" or "disabled" - """ - type: Literal["enabled", "disabled"] - -class WebSearchResultBlock(BaseModel): - """ - Web search result in response. - - Attributes: - type: Always "web_search_result" - url: Result URL - title: Page title - snippet: Text snippet - """ - type: Literal["web_search_result"] - url: str - title: str - snippet: str -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - web_search={"type": "enabled"}, - messages=[ - { - "role": "user", - "content": "What are the latest AI developments in 2025?" - } - ] -) - -# Check for web search results -for block in message.content: - if block.type == "web_search_result": - print(f"Found: {block.title}") - print(f"URL: {block.url}") - print(f"Snippet: {block.snippet}") -``` - -### Notes - -- Requires internet connectivity -- Adds latency to requests (typically 1-3 seconds) -- Results are current at time of request -- May not be available in all regions - -## Code Execution - -Enable Python code execution in secure sandbox environment. - -### API - -```python { .api } -class CodeExecutionConfigParam(TypedDict): - """ - Code execution configuration. - - Fields: - type: "enabled" or "disabled" - """ - type: Literal["enabled", "disabled"] -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - code_execution={"type": "enabled"}, - messages=[ - { - "role": "user", - "content": "Calculate the first 10 Fibonacci numbers." - } - ] -) -``` - -### Notes - -- Runs in isolated sandbox with limited packages -- Security restrictions prevent file system access -- Network access is disabled -- Execution timeout applies (typically 30 seconds) -- Available packages: NumPy, Pandas, Matplotlib, etc. (check docs) - -## Computer Use - -Enable computer interaction capabilities including screenshots, mouse, and keyboard control. - -### API - -```python { .api } -class ComputerUseConfigParam(TypedDict): - """ - Computer use configuration. - - Fields: - type: "enabled" or "disabled" - display_width_px: Display width in pixels - display_height_px: Display height in pixels - display_number: Display number (optional) - """ - type: Literal["enabled", "disabled"] - display_width_px: NotRequired[int] - display_height_px: NotRequired[int] - display_number: NotRequired[int] -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - computer_use={ - "type": "enabled", - "display_width_px": 1920, - "display_height_px": 1080, - }, - messages=[ - { - "role": "user", - "content": "Open a web browser and search for Python tutorials." - } - ] -) -``` - -### Notes - -- Requires display configuration -- Security restrictions apply -- Not available in all regions -- Use with caution - can interact with GUI -- Suitable for automation and testing scenarios - -## Bash Commands - -Enable bash command execution. - -### API - -```python { .api } -class BashConfigParam(TypedDict): - """ - Bash configuration. - - Fields: - type: "enabled" or "disabled" - """ - type: Literal["enabled", "disabled"] - -class ToolBash20250124(TypedDict): - """ - Bash tool definition. - - Fields: - type: Always "bash_20250124" - name: Tool name - """ - type: Literal["bash_20250124"] - name: str -``` - -### Notes - -- Executes shell commands in sandboxed environment -- Security restrictions apply -- Limited access to system resources -- Use with caution in production - -## Text Editor - -Enable text file editing capabilities. - -### API - -```python { .api } -class TextEditorConfigParam(TypedDict): - """ - Text editor configuration. - - Fields: - type: "enabled" or "disabled" - """ - type: Literal["enabled", "disabled"] - -class ToolTextEditor20250124(TypedDict): - """ - Text editor tool (latest version). - - Fields: - type: Always "text_editor_20250124" - name: Tool name - """ - type: Literal["text_editor_20250124"] - name: str -``` - -### Notes - -- Supports text file creation and editing -- Works in sandboxed environment -- File operations are temporary unless persisted -- Suitable for code generation and modification - -## MCP Integration - -Enable Model Context Protocol tool integration for custom external tools. - -### API - -```python { .api } -class BetaRequestMCPServerURLDefinitionParam(TypedDict, total=False): - """MCP server URL definition.""" - url: Required[str] - """URL of the MCP server""" - -class BetaMCPToolsetParam(TypedDict, total=False): - """MCP toolset configuration.""" - servers: list[BetaRequestMCPServerURLDefinitionParam] - """List of MCP server definitions""" - -class BetaMCPToolConfigParam(TypedDict, total=False): - """MCP tool configuration.""" - # Tool-specific configuration - -class BetaMCPToolUseBlock(BaseModel): - """MCP tool use in response.""" - type: Literal["mcp_tool_use"] - # MCP tool execution information - -class BetaMCPToolResultBlock(BaseModel): - """MCP tool result in response.""" - type: Literal["mcp_tool_result"] - # MCP tool result data -``` - -### Example - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - mcp_servers=[ - {"url": "http://localhost:8080/mcp"}, - ], - messages=[ - {"role": "user", "content": "Use the MCP tools to fetch data"} - ] -) - -# Check for MCP tool uses -for block in message.content: - if block.type == "mcp_tool_use": - print(f"MCP tool used: {block}") - elif block.type == "mcp_tool_result": - print(f"MCP result: {block}") -``` - -### Notes - -- Integrates external tools via Model Context Protocol -- Requires running MCP server(s) -- Enables custom tool integration beyond built-in tools -- Supports multiple MCP servers simultaneously -- Advanced feature for specialized use cases - -## Memory Tools - -Enable persistent memory across conversations. - -### API - -```python { .api } -class MemoryConfigParam(TypedDict): - """ - Memory configuration. - - Fields: - type: "enabled" or "disabled" - """ - type: Literal["enabled", "disabled"] - -class BetaAbstractMemoryTool: - """ - Abstract base class for memory tools. - - Subclass to implement custom memory backends. - """ - def store(self, key: str, value: Any) -> None: - """Store value in memory.""" - ... - - def retrieve(self, key: str) -> Any: - """Retrieve value from memory.""" - ... - - def delete(self, key: str) -> None: - """Delete value from memory.""" - ... -``` - -### Example - Custom Memory Implementation - -```python -# Example custom memory tool implementation -class DatabaseMemoryTool(BetaAbstractMemoryTool): - def __init__(self, db_connection): - self.db = db_connection - - def store(self, key: str, value: Any) -> None: - """Store value in database.""" - self.db.execute("INSERT INTO memory (key, value) VALUES (?, ?)", (key, value)) - - def retrieve(self, key: str) -> Any: - """Retrieve value from database.""" - result = self.db.execute("SELECT value FROM memory WHERE key = ?", (key,)).fetchone() - return result[0] if result else None - - def delete(self, key: str) -> None: - """Delete value from database.""" - self.db.execute("DELETE FROM memory WHERE key = ?", (key,)) - -# Use with beta messages -db_memory = DatabaseMemoryTool(db_conn) - -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - memory={"type": "enabled"}, - messages=[ - {"role": "user", "content": "Remember that my favorite color is blue."} - ] -) -``` - -### Notes - -- Enables persistent context across conversations -- Requires custom memory backend implementation -- Useful for multi-turn conversations with state -- Memory persists beyond single request - -## Combined Features Example - -Use multiple beta features together for maximum capability: - -```python -# Use multiple beta features in one request -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=4096, - thinking={"type": "enabled", "budget_tokens": 2000}, - web_search={"type": "enabled"}, - code_execution={"type": "enabled"}, - citations={"type": "enabled"}, - messages=[ - { - "role": "user", - "content": [ - { - "type": "document", - "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data} - }, - { - "type": "text", - "text": "Analyze this research paper, search for recent related work, and write Python code to replicate key results. Include citations." - } - ] - } - ] -) - -# Process response with all beta features -for block in message.content: - if block.type == "thinking": - print(f"Reasoning: {block.thinking}") - elif block.type == "text": - print(f"Analysis: {block.text}") - if hasattr(block, 'citations'): - print("Citations:") - for citation in block.citations: - print(f" - {citation.cited_text}") - elif block.type == "web_search_result": - print(f"Related work: {block.title} ({block.url})") -``` - -## Streaming with Beta Features - -Stream responses while using beta features: - -```python -with client.beta.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - thinking={"type": "enabled"}, - messages=[ - {"role": "user", "content": "Explain quantum computing"} - ] -) as stream: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "thinking_delta": - print(f"[Thinking: {event.delta.thinking}]") - elif event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - print() - -# Get final message -message = stream.get_final_message() -print(f"\nTokens used: {message.usage.output_tokens}") -``` - -## Async Beta Messages - -Use beta features with async client: - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - message = await client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - thinking={"type": "enabled"}, - messages=[ - {"role": "user", "content": "Explain machine learning"} - ] - ) - - for block in message.content: - if block.type == "thinking": - print(f"Thinking: {block.thinking}") - elif block.type == "text": - print(f"Answer: {block.text}") - -asyncio.run(main()) -``` - -## Async Streaming with Beta Features - -Combine async streaming with beta features: - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - async with client.beta.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - thinking={"type": "enabled"}, - web_search={"type": "enabled"}, - messages=[ - {"role": "user", "content": "What are the latest quantum computing breakthroughs?"} - ] - ) as stream: - async for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "thinking_delta": - print(f"[Thinking: {event.delta.thinking}]") - elif event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - print() - -asyncio.run(main()) -``` - -## Tool Runner with Beta Tools - -Use tool runner with beta features: - -```python -from anthropic import beta_tool - -@beta_tool -def search_database(query: str) -> list: - """Search database for query.""" - return [{"id": 1, "title": "Result"}] - -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - tools=[search_database], - messages=[{"role": "user", "content": "Search for Python tutorials"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -## Best Practices - -### 1. Choose Appropriate Features - -Only enable features you need: -```python -# Don't do this (unnecessary features) -message = client.beta.messages.create( - thinking={"type": "enabled"}, - web_search={"type": "enabled"}, - code_execution={"type": "enabled"}, - messages=[{"role": "user", "content": "What is 2+2?"}] # Simple question -) - -# Do this (appropriate features) -message = client.beta.messages.create( - messages=[{"role": "user", "content": "What is 2+2?"}] # No features needed -) -``` - -### 2. Set Reasonable Token Budgets - -For thinking, set budgets appropriate to task complexity: -```python -# Simple task -thinking={"type": "enabled", "budget_tokens": 500} - -# Complex reasoning -thinking={"type": "enabled", "budget_tokens": 2000} -``` - -### 3. Handle Feature-Specific Content - -Check for feature-specific content blocks: -```python -for block in message.content: - if block.type == "thinking": - # Handle reasoning - ... - elif block.type == "web_search_result": - # Handle search result - ... - elif block.type == "text": - # Handle text - ... -``` - -### 4. Test Features Individually - -Test each feature separately before combining: -```python -# Test thinking alone -message1 = client.beta.messages.create( - thinking={"type": "enabled"}, - messages=[...] -) - -# Test web search alone -message2 = client.beta.messages.create( - web_search={"type": "enabled"}, - messages=[...] -) - -# Combine after testing -message3 = client.beta.messages.create( - thinking={"type": "enabled"}, - web_search={"type": "enabled"}, - messages=[...] -) -``` - -### 5. Monitor Costs - -Beta features may have additional costs: -```python -# Track usage -print(f"Input tokens: {message.usage.input_tokens}") -print(f"Output tokens: {message.usage.output_tokens}") -if message.usage.cache_creation_input_tokens: - print(f"Cache creation: {message.usage.cache_creation_input_tokens}") -``` - -## Limitations and Considerations - -### Token Usage -- Extended thinking increases token usage -- Web search adds overhead -- Budget accordingly for production use - -### Latency -- Web search adds 1-3 seconds -- Code execution adds execution time -- Computer use may have delays - -### Availability -- Not all features available in all regions -- Some features require specific models -- Check documentation for current availability - -### Security -- Code execution is sandboxed -- Computer use has security restrictions -- Bash commands are limited -- Use appropriate caution in production - -### Stability -- Beta features may change -- Breaking changes possible -- Monitor for deprecations -- Test after SDK updates - -## See Also - -- [Beta Overview](./index.md) - Overview of all beta features -- [Beta Batches](./batches.md) - Use beta features in batches -- [Messages API](../api/messages.md) - Core message creation -- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md b/.tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md deleted file mode 100644 index a2dcc2df8..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/beta/skills.md +++ /dev/null @@ -1,599 +0,0 @@ -# Beta Skills API - -Create and manage reusable capabilities with version control. - -## Overview - -The Skills API allows you to create, manage, and version reusable capabilities that can be used across conversations. Skills are defined by uploading files including a SKILL.md file that describes the skill's functionality. - -## Key Features - -- Create skills with file uploads -- Version control for skill evolution -- List and filter skills by source (custom or Anthropic-provided) -- Delete skills and specific versions -- Cursor-based pagination for scalability - -## Skills API - -### Create Skill - -```python { .api } -def create( - self, - *, - display_title: str | None = NOT_GIVEN, - files: list[FileTypes] | None = NOT_GIVEN, - **kwargs -) -> SkillCreateResponse: - """ - Create a skill. - - Parameters: - display_title: Display title for the skill (human-readable label, not included in model prompt) - files: Files to upload for the skill - All files must be in the same top-level directory - Must include a SKILL.md file at the root - - Returns: - SkillCreateResponse object - """ - ... -``` - -### Retrieve Skill - -```python { .api } -def retrieve( - self, - skill_id: str, - **kwargs -) -> SkillRetrieveResponse: - """ - Retrieve skill by ID. - - Parameters: - skill_id: Unique identifier for the skill - - Returns: - SkillRetrieveResponse with skill details - """ - ... -``` - -### List Skills - -```python { .api } -def list( - self, - *, - limit: int = NOT_GIVEN, - page: str | None = NOT_GIVEN, - source: str | None = NOT_GIVEN, - **kwargs -) -> SyncPageCursor[SkillListResponse]: - """ - List skills with cursor-based pagination. - - Parameters: - limit: Number of results per page (max 100, default 20) - page: Pagination token from previous response's next_page field - source: Filter by source ("custom" for user-created, "anthropic" for Anthropic-created) - - Returns: - SyncPageCursor[SkillListResponse] with paginated results - """ - ... -``` - -### Delete Skill - -```python { .api } -def delete( - self, - skill_id: str, - **kwargs -) -> SkillDeleteResponse: - """ - Delete a skill. - - Parameters: - skill_id: Unique identifier for the skill - - Returns: - SkillDeleteResponse confirming deletion - """ - ... -``` - -## Skill Versions API - -Manage versions of existing skills for evolution and rollback capabilities. - -### Create Version - -```python { .api } -def create( - self, - skill_id: str, - *, - files: list[FileTypes] | None = NOT_GIVEN, - **kwargs -) -> VersionCreateResponse: - """ - Create a new version of a skill. - - Parameters: - skill_id: Unique identifier for the skill - files: Files to upload for the skill version - Must include a SKILL.md file at the root - - Returns: - VersionCreateResponse with new version details - """ - ... -``` - -### Retrieve Version - -```python { .api } -def retrieve( - self, - version: str, - *, - skill_id: str, - **kwargs -) -> VersionRetrieveResponse: - """ - Get details about a specific skill version. - - Parameters: - version: Version identifier - skill_id: Unique identifier for the skill - - Returns: - VersionRetrieveResponse with version details - """ - ... -``` - -### List Versions - -```python { .api } -def list( - self, - skill_id: str, - *, - limit: int | None = NOT_GIVEN, - page: str | None = NOT_GIVEN, - **kwargs -) -> SyncPageCursor[VersionListResponse]: - """ - List versions of a skill. - - Parameters: - skill_id: Unique identifier for the skill - limit: Number of items to return per page (default 20, range 1-1000) - page: Pagination token from previous response's next_page field - - Returns: - SyncPageCursor[VersionListResponse] with cursor-paginated versions - """ - ... -``` - -### Delete Version - -```python { .api } -def delete( - self, - version: str, - *, - skill_id: str, - **kwargs -) -> VersionDeleteResponse: - """ - Delete a specific skill version. - - Parameters: - version: Version identifier - skill_id: Unique identifier for the skill - - Returns: - VersionDeleteResponse confirming deletion - """ - ... -``` - -## Examples - -### Create Simple Skill - -```python -from anthropic import Anthropic -from anthropic._utils import file_from_path - -client = Anthropic() - -# Create skill with SKILL.md -skill = client.beta.skills.create( - display_title="Weather Analyzer", - files=[ - file_from_path("SKILL.md"), # Required: skill description - file_from_path("weather.py"), # Optional: implementation files - ] -) - -print(f"Created skill: {skill.id}") -print(f"Display title: {skill.display_title}") -``` - -### SKILL.md Format - -```markdown -# Weather Analyzer - -Analyze weather data and provide recommendations. - -## Capabilities - -- Temperature analysis -- Humidity assessment -- Weather recommendations - -## Usage - -Call this skill with temperature and humidity values to get weather analysis. -``` - -### List Skills - -```python -# List all custom skills -for skill in client.beta.skills.list(source="custom"): - print(f"Skill: {skill.display_title} ({skill.id})") - -# List Anthropic-provided skills -for skill in client.beta.skills.list(source="anthropic"): - print(f"Anthropic skill: {skill.display_title}") - -# Paginated listing -page_result = client.beta.skills.list(limit=10) -for skill in page_result: - print(f"Skill: {skill.display_title}") - -# Get next page -if page_result.next_page: - next_page = client.beta.skills.list(limit=10, page=page_result.next_page) -``` - -### Retrieve Skill - -```python -skill = client.beta.skills.retrieve("skill_abc123") -print(f"Name: {skill.display_title}") -print(f"Created: {skill.created_at}") -print(f"Latest version: {skill.latest_version}") -``` - -### Delete Skill - -```python -# Delete entire skill -deleted = client.beta.skills.delete("skill_abc123") -print(f"Deleted skill: {deleted.id}") -``` - -### Create New Version - -```python -# Create new version with updated files -version = client.beta.skills.versions.create( - skill_id="skill_abc123", - files=[ - file_from_path("SKILL.md"), # Updated description - file_from_path("weather_v2.py"), # New implementation - ] -) - -print(f"Created version: {version.version}") -print(f"Version ID: {version.id}") -``` - -### List Versions - -```python -# List all versions of a skill -for version in client.beta.skills.versions.list(skill_id="skill_abc123"): - print(f"Version: {version.version}") - print(f"Created: {version.created_at}") - print(f"Status: {version.status}") - print("---") - -# Paginated version listing -versions = client.beta.skills.versions.list( - skill_id="skill_abc123", - limit=5 -) - -for version in versions: - print(f"Version: {version.version}") - -# Get next page -if versions.next_page: - next_versions = client.beta.skills.versions.list( - skill_id="skill_abc123", - limit=5, - page=versions.next_page - ) -``` - -### Retrieve Specific Version - -```python -version = client.beta.skills.versions.retrieve( - skill_id="skill_abc123", - version="v1" -) - -print(f"Version: {version.version}") -print(f"Created: {version.created_at}") -``` - -### Delete Version - -```python -# Delete specific version -deleted = client.beta.skills.versions.delete( - skill_id="skill_abc123", - version="v1" -) - -print(f"Deleted version: {deleted.version}") -``` - -### Version Management Pattern - -```python -# Create skill -skill = client.beta.skills.create( - display_title="Data Processor", - files=[file_from_path("SKILL.md"), file_from_path("processor_v1.py")] -) - -print(f"Created skill {skill.id} with version {skill.latest_version}") - -# Update skill with new version -v2 = client.beta.skills.versions.create( - skill_id=skill.id, - files=[file_from_path("SKILL.md"), file_from_path("processor_v2.py")] -) - -print(f"Created version {v2.version}") - -# List all versions -versions = list(client.beta.skills.versions.list(skill_id=skill.id)) -print(f"Total versions: {len(versions)}") - -# Rollback by deleting latest version (if needed) -if len(versions) > 1: - latest = versions[0] - client.beta.skills.versions.delete( - skill_id=skill.id, - version=latest.version - ) - print(f"Rolled back to previous version") -``` - -### Multi-File Skill - -```python -# Create skill with multiple files -skill = client.beta.skills.create( - display_title="Advanced Calculator", - files=[ - file_from_path("SKILL.md"), - file_from_path("calculator.py"), - file_from_path("math_utils.py"), - file_from_path("constants.py"), - ] -) - -print(f"Created skill with {len(skill.files)} files") -``` - -### Async Skills Operations - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - # Create skill asynchronously - skill = await client.beta.skills.create( - display_title="Async Processor", - files=[file_from_path("SKILL.md")] - ) - - # List skills - async for skill in client.beta.skills.list(): - print(f"Skill: {skill.display_title}") - - # Create version - version = await client.beta.skills.versions.create( - skill_id=skill.id, - files=[file_from_path("SKILL.md")] - ) - - print(f"Created version: {version.version}") - -asyncio.run(main()) -``` - -### Error Handling - -```python -from anthropic import APIError, BadRequestError - -try: - # Create skill - skill = client.beta.skills.create( - display_title="Test Skill", - files=[file_from_path("SKILL.md")] - ) -except BadRequestError as e: - if "SKILL.md" in str(e): - print("Error: SKILL.md file is required at root") - else: - print(f"Invalid request: {e.message}") -except APIError as e: - print(f"API error: {e.message}") - -# Validate skill exists before operations -try: - skill = client.beta.skills.retrieve("skill_abc123") - print(f"Skill exists: {skill.display_title}") -except APIError: - print("Skill not found") -``` - -## File Requirements - -### SKILL.md File - -- **Required**: Every skill must include a SKILL.md file at the root -- **Purpose**: Describes the skill's capabilities and usage -- **Format**: Markdown file with skill documentation -- **Location**: Must be in the root directory with other skill files - -### File Organization - -- All skill files must be in the same top-level directory -- No subdirectories allowed -- Include all necessary implementation files -- Keep files focused and modular - -### Example Structure - -``` -skill_files/ - ├── SKILL.md # Required: skill description - ├── main.py # Optional: implementation - ├── utils.py # Optional: utilities - └── constants.json # Optional: configuration -``` - -## Best Practices - -### 1. Clear SKILL.md Documentation - -Write comprehensive SKILL.md files: -```markdown -# Skill Name - -Clear one-line description. - -## Capabilities - -- Bullet list of what the skill does -- Specific use cases - -## Usage - -How to invoke and use the skill. - -## Requirements - -Any prerequisites or constraints. -``` - -### 2. Version Management - -- Create new versions for updates instead of deleting and recreating -- Keep version history for rollback capability -- Document changes in SKILL.md for each version - -### 3. Naming Conventions - -Use descriptive display titles: -```python -# Good -display_title="Weather Data Analyzer" - -# Bad -display_title="skill1" -``` - -### 4. File Organization - -Keep related functionality together: -```python -files=[ - file_from_path("SKILL.md"), - file_from_path("analyzer.py"), # Main logic - file_from_path("data_utils.py"), # Helper functions - file_from_path("config.json"), # Configuration -] -``` - -### 5. Error Handling - -Always validate operations: -```python -try: - skill = client.beta.skills.retrieve(skill_id) - # Use skill -except APIError: - # Handle missing skill - pass -``` - -### 6. Pagination - -Use cursor pagination for large lists: -```python -page = None -while True: - results = client.beta.skills.list(limit=100, page=page) - for skill in results: - process_skill(skill) - - if not results.next_page: - break - page = results.next_page -``` - -## Limitations and Considerations - -### File Constraints - -- All files must be in same directory (no subdirectories) -- SKILL.md required at root -- File size limits apply (check API documentation) - -### Version Limits - -- Check API for maximum versions per skill -- Old versions remain until explicitly deleted -- Deleting a skill deletes all versions - -### Source Filtering - -- "custom": User-created skills -- "anthropic": Anthropic-provided skills -- Use source filter to separate your skills from system skills - -### Pagination - -- Default page size: 20 -- Maximum page size: 100 (skills), 1000 (versions) -- Use next_page token for cursor-based pagination - -## See Also - -- [Beta Overview](./index.md) - Overview of all beta features -- [Files API](./files.md) - File upload and management -- [Beta Batches](./batches.md) - Batch processing with beta features -- [Message Features](./message-features.md) - Beta message enhancement features diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md deleted file mode 100644 index fc961b722..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/basic-messaging.md +++ /dev/null @@ -1,336 +0,0 @@ -# Basic Messaging Tasks - -Practical patterns for common messaging scenarios. For complete API reference, see **[Messages API](../api/messages.md)**. - -## Send Simple Text Message - -```python -from anthropic import Anthropic - -client = Anthropic() # Uses ANTHROPIC_API_KEY env var - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[ - {"role": "user", "content": "Explain quantum computing in simple terms"} - ] -) - -print(message.content[0].text) -``` - -## Use System Prompt - -System prompts guide Claude's behavior and style: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - system="You are a helpful Python programming assistant. Be concise and provide code examples.", - messages=[ - {"role": "user", "content": "How do I read a CSV file?"} - ] -) -``` - -### System Prompt with Caching - -Cache long system prompts to reduce costs: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - system=[ - { - "type": "text", - "text": "You are an expert on Shakespeare's works. Here is context:\n\n" + long_context, - "cache_control": {"type": "ephemeral"} - } - ], - messages=[ - {"role": "user", "content": "Analyze Hamlet's soliloquy"} - ] -) - -# Check cache usage -print(f"Cache hits: {message.usage.cache_read_input_tokens}") -print(f"Cache misses: {message.usage.cache_creation_input_tokens}") -``` - -## Multi-Turn Conversation - -Maintain conversation history: - -```python -conversation = [] - -# Turn 1 -conversation.append({"role": "user", "content": "My name is Alice"}) - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=conversation -) - -conversation.append({ - "role": "assistant", - "content": message.content -}) - -# Turn 2 -conversation.append({"role": "user", "content": "What's my name?"}) - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=conversation -) - -print(message.content[0].text) # "Your name is Alice" -``` - -### Conversation Manager Helper - -```python -class ConversationManager: - def __init__(self, client: Anthropic, model: str, system: str = None): - self.client = client - self.model = model - self.system = system - self.history = [] - - def send(self, user_message: str) -> str: - """Send message and update history""" - self.history.append({"role": "user", "content": user_message}) - - message = self.client.messages.create( - model=self.model, - max_tokens=1024, - system=self.system, - messages=self.history - ) - - assistant_message = message.content[0].text - self.history.append({ - "role": "assistant", - "content": message.content - }) - - return assistant_message - -# Usage -conv = ConversationManager( - client, - model="claude-sonnet-4-5-20250929", - system="You are a helpful assistant" -) - -response1 = conv.send("Hello, I'm Bob") -response2 = conv.send("What's my name?") # Remembers "Bob" -``` - -## Control Response Temperature - -Temperature affects randomness (0.0 = deterministic, 1.0 = creative): - -```python -# Deterministic/factual responses -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - temperature=0.0, - messages=[{"role": "user", "content": "What is 2+2?"}] -) - -# Creative/varied responses -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - temperature=1.0, - messages=[{"role": "user", "content": "Write a creative story"}] -) -``` - -## Use Stop Sequences - -Stop generation at specific strings: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - stop_sequences=["", "\n\n---"], - messages=[ - { - "role": "user", - "content": "List 5 colors, each on a new line. End with " - } - ] -) - -# Check if stop sequence was hit -if message.stop_reason == "stop_sequence": - print(f"Stopped at: {message.stop_sequence}") -``` - -## Track Token Usage - -Monitor input and output tokens for cost management: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) - -usage = message.usage -print(f"Input tokens: {usage.input_tokens}") -print(f"Output tokens: {usage.output_tokens}") -print(f"Total: {usage.input_tokens + usage.output_tokens}") - -# With caching -if usage.cache_read_input_tokens: - print(f"Cache hits: {usage.cache_read_input_tokens} tokens") -if usage.cache_creation_input_tokens: - print(f"New cache entries: {usage.cache_creation_input_tokens} tokens") -``` - -## Count Tokens Before Sending - -Estimate costs without creating a message: - -```python -token_count = client.messages.count_tokens( - model="claude-sonnet-4-5-20250929", - messages=[ - {"role": "user", "content": "Very long message..."} - ] -) - -print(f"This request will use {token_count.input_tokens} input tokens") - -# Check if within budget -MAX_TOKENS = 100000 -if token_count.input_tokens > MAX_TOKENS: - print("Message too long, truncating...") -``` - -## Add Request Metadata - -Track users for rate limiting and abuse prevention: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - metadata={"user_id": "user_12345"}, - messages=[{"role": "user", "content": "Hello"}] -) -``` - -## Handle Max Tokens Limit - -Deal with incomplete responses: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=100, # Intentionally small - messages=[{"role": "user", "content": "Write a long essay"}] -) - -if message.stop_reason == "max_tokens": - print("Response was truncated. Consider increasing max_tokens.") - print(f"Partial response: {message.content[0].text}") - - # Continue generation - continued = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[ - {"role": "user", "content": "Write a long essay"}, - {"role": "assistant", "content": message.content}, - {"role": "user", "content": "Please continue"} - ] - ) -``` - -## Use Different Models - -Choose model based on requirements: - -```python -# Fast and cost-effective -message = client.messages.create( - model="claude-3-5-haiku-20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Quick question"}] -) - -# Balanced (recommended for most tasks) -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Standard query"}] -) - -# Maximum capability for complex tasks -message = client.messages.create( - model="claude-opus-4-5-20250929", - max_tokens=2048, - messages=[{"role": "user", "content": "Complex reasoning task"}] -) -``` - -## Async Messaging - -For async applications: - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def send_message(content: str) -> str: - client = AsyncAnthropic() - - message = await client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": content}] - ) - - return message.content[0].text - -# Run async function -response = asyncio.run(send_message("Hello")) - -# Concurrent requests -async def send_multiple(): - client = AsyncAnthropic() - - tasks = [ - client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": f"Question {i}"}] - ) - for i in range(5) - ] - - responses = await asyncio.gather(*tasks) - return [r.content[0].text for r in responses] - -results = asyncio.run(send_multiple()) -``` - -## See Also - -- **[Messages API Reference](../api/messages.md)** - Complete API documentation -- **[Multimodal Input](./multimodal-input.md)** - Working with images and documents -- **[Streaming Responses](./streaming-responses.md)** - Real-time streaming -- **[Error Handling Guide](../guides/error-handling.md)** - Production error patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md deleted file mode 100644 index 16b426b64..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/multimodal-input.md +++ /dev/null @@ -1,421 +0,0 @@ -# Multimodal Input Tasks - -Practical patterns for working with images, PDFs, and documents. For complete reference, see **[Multimodal Guide](../guides/multimodal.md)**. - -## Analyze Image from File - -```python -import base64 -from anthropic import Anthropic - -client = Anthropic() - -# Read and encode image -with open("image.jpg", "rb") as f: - image_data = base64.standard_b64encode(f.read()).decode() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "image", - "source": { - "type": "base64", - "media_type": "image/jpeg", - "data": image_data - } - }, - {"type": "text", "text": "What's in this image?"} - ] - }] -) - -print(message.content[0].text) -``` - -## Supported Image Formats - -```python -# JPEG -with open("photo.jpg", "rb") as f: - data = base64.standard_b64encode(f.read()).decode() - source = {"type": "base64", "media_type": "image/jpeg", "data": data} - -# PNG -with open("screenshot.png", "rb") as f: - data = base64.standard_b64encode(f.read()).decode() - source = {"type": "base64", "media_type": "image/png", "data": data} - -# GIF -with open("animation.gif", "rb") as f: - data = base64.standard_b64encode(f.read()).decode() - source = {"type": "base64", "media_type": "image/gif", "data": data} - -# WebP -with open("image.webp", "rb") as f: - data = base64.standard_b64encode(f.read()).decode() - source = {"type": "base64", "media_type": "image/webp", "data": data} -``` - -## Analyze Multiple Images - -Compare or analyze multiple images together: - -```python -import base64 - -def load_image(path: str) -> str: - with open(path, "rb") as f: - return base64.standard_b64encode(f.read()).decode() - -img1 = load_image("before.jpg") -img2 = load_image("after.jpg") - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "text", "text": "Before:"}, - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img1}}, - {"type": "text", "text": "After:"}, - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img2}}, - {"type": "text", "text": "What changed between these images?"} - ] - }] -) -``` - -## Optimize Large Images - -Reduce token usage by resizing images: - -```python -from PIL import Image -import io -import base64 - -def optimize_image(image_path: str, max_size=(1024, 1024)) -> str: - """Resize and optimize image for API""" - img = Image.open(image_path) - - # Resize maintaining aspect ratio - img.thumbnail(max_size, Image.Resampling.LANCZOS) - - # Convert to JPEG with compression - buffer = io.BytesIO() - img = img.convert("RGB") # Ensure RGB mode - img.save(buffer, format="JPEG", quality=85, optimize=True) - - return base64.standard_b64encode(buffer.getvalue()).decode() - -# Use optimized image -optimized_data = optimize_image("large_photo.jpg") - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": optimized_data}}, - {"type": "text", "text": "Analyze this image"} - ] - }] -) -``` - -## Process PDF Document - -```python -import base64 - -with open("document.pdf", "rb") as f: - pdf_data = base64.standard_b64encode(f.read()).decode() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": pdf_data - } - }, - {"type": "text", "text": "Summarize the key points in this document"} - ] - }] -) -``` - -## PDF from URL - -Process publicly accessible PDFs without downloading: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "url", - "media_type": "application/pdf", - "url": "https://example.com/document.pdf" - } - }, - {"type": "text", "text": "Extract the main findings"} - ] - }] -) -``` - -## Process Plain Text Document - -For large text content: - -```python -long_text = """Very long text content...""" - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "text", - "media_type": "text/plain", - "data": long_text - } - }, - {"type": "text", "text": "Analyze the sentiment of this text"} - ] - }] -) -``` - -## Mix Text, Images, and Documents - -Combine multiple content types: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - {"type": "text", "text": "Here's a presentation:"}, - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": pdf_data - } - }, - {"type": "text", "text": "And the cover image:"}, - { - "type": "image", - "source": { - "type": "base64", - "media_type": "image/jpeg", - "data": image_data - } - }, - {"type": "text", "text": "Review both and provide feedback"} - ] - }] -) -``` - -## Common Image Analysis Tasks - -### Extract Text (OCR) - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_data}}, - {"type": "text", "text": "Extract all text from this image"} - ] - }] -) -``` - -### Identify Objects - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_data}}, - {"type": "text", "text": "List all objects visible in this image"} - ] - }] -) -``` - -### Answer Questions About Image - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": image_data}}, - {"type": "text", "text": "Is there a person in this image? If so, describe them."} - ] - }] -) -``` - -## Common Document Tasks - -### Summarize Document - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, - {"type": "text", "text": "Provide a 3-paragraph summary"} - ] - }] -) -``` - -### Q&A on Document - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, - {"type": "text", "text": "What methodology was used in this study?"} - ] - }] -) -``` - -### Extract Specific Information - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, - {"type": "text", "text": "Extract all dates, names, and monetary amounts mentioned"} - ] - }] -) -``` - -## Use Citations (Beta) - -Get source attribution for document-based responses: - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - citations={"type": "enabled"}, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, - {"type": "text", "text": "Summarize with citations"} - ] - }] -) - -# Check citations -for block in message.content: - if hasattr(block, 'citations'): - for citation in block.citations: - print(f"Cited: {citation.cited_text}") -``` - -## Helper Functions - -### Load Multiple Images - -```python -import base64 -from pathlib import Path - -def load_images(directory: str) -> list[dict]: - """Load all images from directory""" - content = [] - - for path in Path(directory).glob("*.{jpg,jpeg,png}"): - with open(path, "rb") as f: - data = base64.standard_b64encode(f.read()).decode() - - # Determine media type from extension - media_type = { - ".jpg": "image/jpeg", - ".jpeg": "image/jpeg", - ".png": "image/png" - }[path.suffix.lower()] - - content.append({ - "type": "image", - "source": { - "type": "base64", - "media_type": media_type, - "data": data - } - }) - - return content - -# Use it -images = load_images("./photos") -images.append({"type": "text", "text": "Describe all these images"}) - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{"role": "user", "content": images}] -) -``` - -## See Also - -- **[Multimodal Guide](../guides/multimodal.md)** - Complete multimodal documentation -- **[Messages API Reference](../api/messages.md)** - API details -- **[Beta Citations](../beta/message-features.md#citations)** - Source attribution feature diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md deleted file mode 100644 index f0d3c7d32..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/streaming-responses.md +++ /dev/null @@ -1,450 +0,0 @@ -# Streaming Responses Tasks - -Practical patterns for real-time streaming. For complete reference, see **[Streaming API](../api/streaming.md)** and **[Streaming Guide](../guides/streaming-guide.md)**. - -## Basic Text Streaming - -```python -from anthropic import Anthropic - -client = Anthropic() - -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a short story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -print() # New line after stream ends -``` - -**That's it!** The `.text_stream` property automatically filters out non-text events and gives you text deltas ready to print. - -## Get Final Message After Streaming - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "What is 2+2?"}] -) as stream: - # Stream the text - for text in stream.text_stream: - print(text, end="", flush=True) - -# Get complete message with metadata -message = stream.get_final_message() -print(f"\n\nToken usage: {message.usage.output_tokens}") -print(f"Stop reason: {message.stop_reason}") -``` - -## Process All Events - -For more control, iterate over all stream events: - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) as stream: - for event in stream: - if event.type == "message_start": - print(f"[Stream started: {event.message.id}]") - - elif event.type == "content_block_start": - print(f"\n[Content block {event.index} started]") - - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - - elif event.type == "content_block_stop": - print(f"\n[Content block {event.index} stopped]") - - elif event.type == "message_delta": - print(f"\n[Stop reason: {event.delta.stop_reason}]") - print(f"[Tokens used: {event.usage.output_tokens}]") - - elif event.type == "message_stop": - print("\n[Stream completed]") -``` - -## Stream with Tool Use - -Detect when Claude wants to use tools: - -```python -tools = [{ - "name": "get_weather", - "description": "Get weather", - "input_schema": { - "type": "object", - "properties": { - "location": {"type": "string"} - }, - "required": ["location"] - } -}] - -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=tools, - messages=[{"role": "user", "content": "What's the weather in SF?"}] -) as stream: - for event in stream: - if event.type == "content_block_start": - if event.content_block.type == "tool_use": - print(f"\n[Tool call: {event.content_block.name}]") - - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - elif event.delta.type == "input_json_delta": - print(event.delta.partial_json, end="") - -message = stream.get_final_message() - -# Process tool calls -for block in message.content: - if block.type == "tool_use": - print(f"\nTool: {block.name}") - print(f"Input: {block.input}") -``` - -## Track Token Usage During Streaming - -Monitor token usage in real-time: - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a long essay"}] -) as stream: - current_tokens = 0 - - for event in stream: - if event.type == "message_delta": - current_tokens = event.usage.output_tokens - print(f"\r[Tokens: {current_tokens}]", end="") - - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) -``` - -## Async Streaming - -For async applications: - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def stream_response(): - client = AsyncAnthropic() - - async with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a haiku"}] - ) as stream: - async for text in stream.text_stream: - print(text, end="", flush=True) - print() - -asyncio.run(stream_response()) -``` - -## Concurrent Async Streams - -Run multiple streams in parallel: - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def stream_question(client: AsyncAnthropic, question: str) -> str: - """Stream a question and return final text""" - async with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": question}] - ) as stream: - # Consume stream - async for _ in stream: - pass - return stream.get_final_text() - -async def main(): - client = AsyncAnthropic() - - questions = [ - "What is Python?", - "What is JavaScript?", - "What is Rust?" - ] - - # Run all streams concurrently - results = await asyncio.gather(*[ - stream_question(client, q) for q in questions - ]) - - for question, answer in zip(questions, results): - print(f"\nQ: {question}") - print(f"A: {answer}") - -asyncio.run(main()) -``` - -## Buffered Streaming - -Buffer output for smoother display: - -```python -import time - -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a story"}] -) as stream: - buffer = "" - - for text in stream.text_stream: - buffer += text - - # Flush every 10 characters or at punctuation - if len(buffer) >= 10 or text in ".!?\n": - print(buffer, end="", flush=True) - buffer = "" - time.sleep(0.02) # Smooth animation - - # Flush remaining - if buffer: - print(buffer, end="", flush=True) -``` - -## Current Message Snapshot - -Get partial message during streaming: - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Count to 10"}] -) as stream: - for event in stream: - # Access current accumulated message - snapshot = stream.current_message_snapshot - - if snapshot.content: - current_text = snapshot.content[0].text if snapshot.content[0].type == "text" else "" - print(f"\rCurrent length: {len(current_text)}", end="") -``` - -## Error Handling in Streams - -```python -from anthropic import APIError, APITimeoutError - -try: - with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -except APITimeoutError: - print("\n[Stream timed out]") -except APIError as e: - print(f"\n[Stream error: {e.message}]") -``` - -## Multi-Turn Conversation with Streaming - -```python -conversation = [] - -def stream_turn(user_message: str): - """Stream a conversation turn""" - conversation.append({"role": "user", "content": user_message}) - - print(f"\nUser: {user_message}") - print("Claude: ", end="") - - with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=conversation - ) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - - message = stream.get_final_message() - conversation.append({ - "role": "assistant", - "content": message.content - }) - print() # New line - -# Conversation -stream_turn("Hi, I'm Alice") -stream_turn("What's my name?") # Claude remembers "Alice" -``` - -## Stream with System Prompt - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - system="You are a helpful Python expert. Be concise.", - messages=[{"role": "user", "content": "Explain list comprehensions"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -## Stream with Temperature - -```python -# Creative streaming -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - temperature=0.8, - messages=[{"role": "user", "content": "Write a creative story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -## Stream Beta Features - -Stream with extended thinking (beta): - -```python -with client.beta.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - thinking={"type": "enabled", "budget_tokens": 1000}, - messages=[{"role": "user", "content": "Solve this complex problem: ..."}] -) as stream: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "thinking_delta": - print(f"[Thinking: {event.delta.thinking}]", end="") - elif event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) -``` - -## Access Raw HTTP Response - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) as stream: - # Access underlying HTTP response - request_id = stream.response.headers.get("request-id") - print(f"[Request ID: {request_id}]") - - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -## Interrupt Streaming - -Handle keyboard interrupts gracefully: - -```python -import signal -import sys - -def signal_handler(sig, frame): - print("\n[Streaming interrupted]") - sys.exit(0) - -signal.signal(signal.SIGINT, signal_handler) - -try: - with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a very long story"}] - ) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -except KeyboardInterrupt: - print("\n[Streaming stopped]") -``` - -## Manual Stream Iteration - -For advanced use cases without context manager: - -```python -stream_manager = client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) - -stream = stream_manager.__enter__() - -try: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - - message = stream.get_final_message() -finally: - stream_manager.__exit__(None, None, None) -``` - -## Streaming Best Practices - -### 1. Always Use Context Managers - -```python -# Good - automatic cleanup -with client.messages.stream(...) as stream: - for text in stream.text_stream: - print(text, end="") - -# Bad - manual cleanup required -stream = client.messages.stream(...) -# ... easy to forget cleanup -``` - -### 2. Set Appropriate Timeouts - -```python -import httpx - -client = Anthropic( - timeout=httpx.Timeout(120.0) # 2 minutes for long streams -) -``` - -### 3. Handle Interruptions - -Always handle potential interruptions gracefully for better UX. - -### 4. Use Async for High Concurrency - -When handling many concurrent streams, use `AsyncAnthropic` for better performance. - -## See Also - -- **[Streaming API Reference](../api/streaming.md)** - Complete event types and architecture -- **[Streaming Guide](../guides/streaming-guide.md)** - Advanced patterns -- **[Messages API](../api/messages.md)** - Core message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md b/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md deleted file mode 100644 index 08637d1ed..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/common-tasks/tool-integration.md +++ /dev/null @@ -1,484 +0,0 @@ -# Tool Integration Tasks - -Practical patterns for function calling and agentic workflows. For complete reference, see **[Tools API](../api/tools.md)** and **[Tool Usage Guide](../guides/tool-usage.md)**. - -## Quick Start with Tool Decorator - -```python -from anthropic import Anthropic, beta_tool - -client = Anthropic() - -# Define tool with decorator -@beta_tool -def get_weather(location: str, unit: str = "fahrenheit") -> dict: - """ - Get weather for a location. - - Args: - location: City and state, e.g. "San Francisco, CA" - unit: Temperature unit - "celsius" or "fahrenheit" - """ - # Your implementation here - return { - "location": location, - "temperature": 72, - "unit": unit, - "condition": "sunny" - } - -# Auto-execute tools with tool_runner -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather], - messages=[{"role": "user", "content": "What's the weather in NYC?"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -**That's it!** The `tool_runner` automatically: -- Sends the request to Claude -- Detects when Claude wants to use a tool -- Executes the tool function -- Sends results back to Claude -- Continues until Claude responds with text - -## Manual Tool Handling - -For more control over the execution flow: - -```python -# 1. Define tool manually -tools = [{ - "name": "get_weather", - "description": "Get weather for a location", - "input_schema": { - "type": "object", - "properties": { - "location": {"type": "string", "description": "City and state"} - }, - "required": ["location"] - } -}] - -# 2. Send initial request -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=tools, - messages=[{"role": "user", "content": "What's the weather in SF?"}] -) - -# 3. Check if Claude wants to use a tool -if message.stop_reason == "tool_use": - # Extract tool use - tool_use = next(block for block in message.content if block.type == "tool_use") - print(f"Claude wants to use: {tool_use.name}") - print(f"With params: {tool_use.input}") - - # 4. Execute the tool - result = get_weather(location=tool_use.input["location"]) - - # 5. Send result back to Claude - final_message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=tools, - messages=[ - {"role": "user", "content": "What's the weather in SF?"}, - {"role": "assistant", "content": message.content}, - { - "role": "user", - "content": [{ - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": str(result) - }] - } - ] - ) - - print(final_message.content[0].text) -``` - -## Multiple Tools - -Claude can choose from multiple tools: - -```python -@beta_tool -def get_weather(location: str) -> dict: - """Get current weather for a location""" - return {"temp": 72, "condition": "sunny"} - -@beta_tool -def search_database(query: str, limit: int = 10) -> list: - """Search database for items matching query""" - return [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}] - -@beta_tool -def send_email(to: str, subject: str, body: str) -> dict: - """Send an email""" - return {"status": "sent", "message_id": "abc123"} - -# Claude picks the right tool(s) -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather, search_database, send_email], - messages=[{"role": "user", "content": "Check the weather in NYC and email it to bob@example.com"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -## Async Tools - -For I/O-bound operations: - -```python -import httpx -from anthropic import AsyncAnthropic, beta_async_tool - -client = AsyncAnthropic() - -@beta_async_tool -async def fetch_url(url: str) -> str: - """Fetch content from a URL""" - async with httpx.AsyncClient() as http_client: - response = await http_client.get(url) - return response.text - -# Use with async tool_runner -async for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[fetch_url], - messages=[{"role": "user", "content": "Fetch https://example.com and summarize it"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -## Control Tool Selection - -### Force Tool Use - -Require Claude to use at least one tool: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "any"}, # Force any tool - messages=[{"role": "user", "content": "Hello"}] -) -``` - -### Force Specific Tool - -Require Claude to use a specific tool: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "tool", "name": "get_weather"}, # Force specific tool - messages=[{"role": "user", "content": "Hello"}] -) -``` - -### Disable Tools for a Turn - -Temporarily disable tool use: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "none"}, # No tools this turn - messages=[...] -) -``` - -### Disable Parallel Tool Use - -Force sequential tool calls: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={ - "type": "auto", - "disable_parallel_tool_use": True - }, - messages=[{"role": "user", "content": "Do task A and then task B"}] -) -``` - -## Error Handling in Tools - -Report tool execution failures to Claude: - -```python -@beta_tool -def divide(a: float, b: float) -> float: - """Divide two numbers""" - if b == 0: - raise ValueError("Cannot divide by zero") - return a / b - -# Manual handling with error reporting -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[divide.to_param()], - messages=[{"role": "user", "content": "What is 10 divided by 0?"}] -) - -tool_use = next(block for block in message.content if block.type == "tool_use") - -# Execute and catch errors -try: - result = divide(**tool_use.input) - tool_result = str(result) - is_error = False -except Exception as e: - tool_result = str(e) - is_error = True - -# Send error back to Claude -final_message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[divide.to_param()], - messages=[ - {"role": "user", "content": "What is 10 divided by 0?"}, - {"role": "assistant", "content": message.content}, - { - "role": "user", - "content": [{ - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": tool_result, - "is_error": is_error # Tell Claude this is an error - }] - } - ] -) - -print(final_message.content[0].text) # Claude handles the error gracefully -``` - -## Stateful Tools - -Tools that maintain state: - -```python -class Calculator: - def __init__(self): - self.memory = 0 - - @beta_tool - def calculate(self, expression: str) -> float: - """Evaluate a mathematical expression and store result""" - result = eval(expression) # Use safe evaluation in production - self.memory = result - return result - - @beta_tool - def recall(self) -> float: - """Recall the last calculation result""" - return self.memory - - @beta_tool - def clear(self) -> str: - """Clear calculator memory""" - self.memory = 0 - return "Memory cleared" - -# Use stateful tools -calc = Calculator() - -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[calc.calculate, calc.recall, calc.clear], - messages=[{"role": "user", "content": "Calculate 5 * 8, then add 10 to that result"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) - print(f"Final memory: {calc.memory}") -``` - -## Validate Tool Inputs - -```python -@beta_tool -def set_temperature(degrees: float, unit: str = "celsius") -> dict: - """Set thermostat temperature""" - # Validate unit - if unit not in ["celsius", "fahrenheit"]: - raise ValueError(f"Invalid unit: {unit}. Must be 'celsius' or 'fahrenheit'") - - # Validate range - if degrees < -50 or degrees > 50: - raise ValueError(f"Temperature {degrees} out of safe range (-50 to 50)") - - # Set temperature - return {"status": "success", "temperature": degrees, "unit": unit} -``` - -## Return Structured Data - -```python -from typing import TypedDict - -class UserInfo(TypedDict): - id: str - name: str - email: str - created_at: str - -@beta_tool -def get_user_info(user_id: str) -> UserInfo: - """Get user information by ID""" - # Fetch from database - return { - "id": user_id, - "name": "Alice Smith", - "email": "alice@example.com", - "created_at": "2024-01-15" - } - -# Claude can work with structured data -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_user_info], - messages=[{"role": "user", "content": "Get info for user user_123 and send them a welcome email"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -## Complex Tool Example - -Database query tool with validation: - -```python -@beta_tool -def query_database( - table: str, - filters: dict, - limit: int = 10, - sort_by: str | None = None -) -> list[dict]: - """ - Query database with filters. - - Args: - table: Table name to query - filters: Key-value pairs for filtering (e.g., {"status": "active"}) - limit: Maximum number of results (1-100) - sort_by: Optional field to sort by - """ - # Validate inputs - valid_tables = ["users", "orders", "products"] - if table not in valid_tables: - raise ValueError(f"Invalid table. Must be one of: {valid_tables}") - - if not 1 <= limit <= 100: - raise ValueError("Limit must be between 1 and 100") - - # Execute query (pseudocode) - results = db.query(table).filter(**filters).limit(limit) - if sort_by: - results = results.sort(sort_by) - - return results.all() -``` - -## Tool Usage Best Practices - -### 1. Clear Descriptions - -Write clear docstrings - Claude uses these to decide when to call the tool: - -```python -@beta_tool -def search_products( - query: str, - category: str | None = None, - min_price: float | None = None, - max_price: float | None = None, - in_stock_only: bool = True -) -> list[dict]: - """ - Search product catalog for matching items. - - Use this tool when the user wants to find or browse products. - - Args: - query: Search keywords or product name - category: Optional category filter (e.g., "electronics", "books", "clothing") - min_price: Minimum price in USD - max_price: Maximum price in USD - in_stock_only: Only show products currently in stock - """ - ... -``` - -### 2. Use Type Hints - -Type hints improve schema generation: - -```python -from typing import Literal - -@beta_tool -def book_appointment( - date: str, # Use specific format hints in docstring - time: str, - service: Literal["haircut", "coloring", "styling"] -) -> dict: - """ - Book an appointment. - - Args: - date: Date in YYYY-MM-DD format - time: Time in HH:MM format (24-hour) - service: Type of service - """ - ... -``` - -### 3. Return Useful Data - -Return structured data that Claude can work with: - -```python -@beta_tool -def get_order_status(order_id: str) -> dict: - """Get status of an order""" - return { - "order_id": order_id, - "status": "shipped", - "tracking_number": "1Z999AA10123456784", - "estimated_delivery": "2024-01-20", - "items_count": 3 - } -``` - -## See Also - -- **[Tools API Reference](../api/tools.md)** - Complete API documentation -- **[Tool Usage Guide](../guides/tool-usage.md)** - Advanced patterns and examples -- **[Messages API](../api/messages.md)** - Core message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md deleted file mode 100644 index 32996caad..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/batch-processing.md +++ /dev/null @@ -1,288 +0,0 @@ -# Batch Processing Guide - -Process thousands of messages asynchronously with 50% cost reduction using the Message Batches API. - -## Why Use Batches? - -- **Cost Reduction**: 50% lower cost compared to standard API -- **High Throughput**: Process thousands of requests asynchronously -- **No Rate Limits**: Batch requests don't count against rate limits - -## Basic Batch - -```python -batch = client.messages.batches.create( - requests=[ - { - "custom_id": "request-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": "What is AI?"}] - } - }, - { - "custom_id": "request-2", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": "What is ML?"}] - } - } - ] -) - -print(f"Batch ID: {batch.id}") -``` - -## Check Status - -```python -batch = client.messages.batches.retrieve("batch_abc123") - -print(f"Status: {batch.processing_status}") -print(f"Succeeded: {batch.request_counts.succeeded}/{batch.request_counts.processing + batch.request_counts.succeeded}") -print(f"Errored: {batch.request_counts.errored}") -``` - -## Poll Until Complete - -```python -import time - -batch_id = "batch_abc123" - -while True: - batch = client.messages.batches.retrieve(batch_id) - - if batch.processing_status == "ended": - break - - print(f"Processing: {batch.request_counts.processing} requests remaining") - time.sleep(60) # Check every minute - -print("Batch complete!") -``` - -## Get Results - -```python -results = client.messages.batches.results(batch_id) - -for response in results: - if response.result.type == "succeeded": - print(f"{response.custom_id}: {response.result.message.content[0].text}") - elif response.result.type == "errored": - print(f"{response.custom_id}: Error - {response.result.error.message}") -``` - -## Process Results - -```python -results_by_id = {} - -for response in client.messages.batches.results(batch_id): - if response.result.type == "succeeded": - results_by_id[response.custom_id] = response.result.message.content[0].text - elif response.result.type == "errored": - results_by_id[response.custom_id] = f"Error: {response.result.error.message}" - -# Access results by custom_id -print(results_by_id["request-1"]) -``` - -## Large-Scale Batch - -```python -# Generate batch requests -requests = [] -for i, question in enumerate(questions): # thousands of questions - requests.append({ - "custom_id": f"question-{i}", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": question}] - } - }) - -# Create batch -batch = client.messages.batches.create(requests=requests) - -# Poll for completion -while True: - batch = client.messages.batches.retrieve(batch.id) - if batch.processing_status == "ended": - break - time.sleep(60) - -# Process results -for response in client.messages.batches.results(batch.id): - save_result(response.custom_id, response.result) -``` - -## Cancel Batch - -```python -batch = client.messages.batches.cancel("batch_abc123") -print(f"Status: {batch.processing_status}") # "canceling" -``` - -## Delete Batch - -```python -# Must be ended first -deleted = client.messages.batches.delete("batch_abc123") -print(f"Deleted: {deleted.id}") -``` - -## List Batches - -```python -# List all batches -for batch in client.messages.batches.list(limit=10): - print(f"{batch.id}: {batch.processing_status}") - -# Paginate manually -page = client.messages.batches.list(limit=10) -for batch in page.data: - print(batch.id) - -if page.has_next_page(): - next_page = page.get_next_page() -``` - -## Batch with Tools - -```python -batch = client.messages.batches.create( - requests=[ - { - "custom_id": "weather-sf", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "tools": [{ - "name": "get_weather", - "description": "Get weather", - "input_schema": { - "type": "object", - "properties": {"location": {"type": "string"}}, - "required": ["location"] - } - }], - "messages": [{"role": "user", "content": "What's the weather in SF?"}] - } - } - ] -) -``` - -## Batch with Streaming Context - -While batches don't stream, you can prepare stream-like prompts: - -```python -requests = [] -for doc in documents: - requests.append({ - "custom_id": f"doc-{doc['id']}", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 2048, - "messages": [{ - "role": "user", - "content": [ - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": doc['data']}}, - {"type": "text", "text": "Summarize this document"} - ] - }] - } - }) -``` - -## Async Batch Operations - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - # Create batch - batch = await client.messages.batches.create(requests=[...]) - - # Poll until complete - while True: - batch = await client.messages.batches.retrieve(batch.id) - if batch.processing_status == "ended": - break - await asyncio.sleep(60) - - # Get results - results = [] - async for response in client.messages.batches.results(batch.id): - results.append(response) - - return results - -results = asyncio.run(main()) -``` - -## Best Practices - -### 1. Use Meaningful Custom IDs - -```python -requests = [{ - "custom_id": f"user-{user_id}-question-{question_id}", - "params": {...} -}] -``` - -### 2. Handle Errors Gracefully - -```python -for response in client.messages.batches.results(batch_id): - if response.result.type == "succeeded": - process_success(response) - elif response.result.type == "errored": - log_error(response.custom_id, response.result.error) - retry_if_needed(response.custom_id) -``` - -### 3. Batch Size Considerations - -- Batches can contain up to 10,000 requests -- Consider splitting very large workloads into multiple batches - -### 4. Monitor Progress - -```python -def monitor_batch(batch_id): - while True: - batch = client.messages.batches.retrieve(batch_id) - - total = sum([ - batch.request_counts.processing, - batch.request_counts.succeeded, - batch.request_counts.errored - ]) - - completed = batch.request_counts.succeeded + batch.request_counts.errored - progress = (completed / total * 100) if total > 0 else 0 - - print(f"Progress: {progress:.1f}% ({completed}/{total})") - - if batch.processing_status == "ended": - break - - time.sleep(60) -``` - -## See Also - -- [Batches API](../api/batches.md) - Complete API reference -- [Messages API](../api/messages.md) - Message creation -- [Beta Features](../beta/index.md) - Beta message batches diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md deleted file mode 100644 index 03f65c0b5..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/error-handling.md +++ /dev/null @@ -1,324 +0,0 @@ -# Error Handling Guide - -Robust error handling patterns for production applications using the Anthropic Python SDK. - -## Basic Error Handling - -```python -from anthropic import APIError - -try: - message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) -except APIError as e: - print(f"Error: {e.message}") -``` - -## Specific Error Types - -```python -from anthropic import ( - RateLimitError, - AuthenticationError, - BadRequestError, - NotFoundError, - InternalServerError, -) - -try: - message = client.messages.create(...) -except RateLimitError as e: - retry_after = e.response.headers.get("retry-after") - print(f"Rate limited. Retry after {retry_after}s") -except AuthenticationError: - print("Invalid API key") -except BadRequestError as e: - print(f"Invalid request: {e.message}") -except NotFoundError: - print("Resource not found") -except InternalServerError: - print("Server error, please retry") -``` - -## Retry with Exponential Backoff - -```python -import time -from anthropic import RateLimitError, InternalServerError - -def create_message_with_retry(max_retries=3, base_delay=1.0): - """Create message with exponential backoff retry.""" - for attempt in range(max_retries): - try: - return client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) - except (RateLimitError, InternalServerError) as e: - if attempt == max_retries - 1: - raise - - wait_time = base_delay * (2 ** attempt) - print(f"Retry {attempt + 1}/{max_retries} after {wait_time}s") - time.sleep(wait_time) - -message = create_message_with_retry() -``` - -## Advanced Retry Pattern - -```python -import random -import time -from anthropic import APIError, RateLimitError - -def exponential_backoff_retry( - func, - max_retries=5, - base_delay=1.0, - max_delay=60.0, - jitter=True -): - """ - Execute function with exponential backoff retry. - - Args: - func: Function to execute - max_retries: Maximum retry attempts - base_delay: Initial delay in seconds - max_delay: Maximum delay in seconds - jitter: Add random jitter to avoid thundering herd - """ - for attempt in range(max_retries): - try: - return func() - except RateLimitError as e: - if attempt == max_retries - 1: - raise - - # Use retry-after header if available - retry_after = e.response.headers.get("retry-after") - if retry_after: - wait_time = float(retry_after) - else: - wait_time = min(base_delay * (2 ** attempt), max_delay) - if jitter: - wait_time *= (0.5 + random.random()) - - print(f"Rate limited. Waiting {wait_time:.1f}s (attempt {attempt + 1}/{max_retries})") - time.sleep(wait_time) - except APIError as e: - if attempt == max_retries - 1: - raise - print(f"API error: {e.message}. Retrying...") - time.sleep(base_delay) - -# Usage -message = exponential_backoff_retry( - lambda: client.messages.create(...) -) -``` - -## Async Error Handling - -```python -import asyncio -from anthropic import AsyncAnthropic, APIError - -async def create_message_safe(): - client = AsyncAnthropic() - - try: - message = await client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) - return message - except APIError as e: - print(f"Error: {e.message}") - return None - -result = asyncio.run(create_message_safe()) -``` - -## Circuit Breaker Pattern - -```python -import time -from anthropic import APIError - -class CircuitBreaker: - """Circuit breaker to prevent cascading failures.""" - - def __init__(self, failure_threshold=5, timeout=60): - self.failure_count = 0 - self.failure_threshold = failure_threshold - self.timeout = timeout - self.last_failure_time = None - self.is_open = False - - def call(self, func, *args, **kwargs): - """Execute function with circuit breaker protection.""" - if self.is_open: - # Check if timeout has passed - if time.time() - self.last_failure_time < self.timeout: - raise Exception("Circuit breaker is open") - else: - # Try to close circuit - self.is_open = False - self.failure_count = 0 - - try: - result = func(*args, **kwargs) - self.failure_count = 0 # Reset on success - return result - except APIError as e: - self.failure_count += 1 - self.last_failure_time = time.time() - - if self.failure_count >= self.failure_threshold: - self.is_open = True - print(f"Circuit breaker opened after {self.failure_count} failures") - - raise - -# Usage -circuit_breaker = CircuitBreaker() - -try: - message = circuit_breaker.call( - client.messages.create, - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) -except Exception as e: - print(f"Circuit breaker error: {e}") -``` - -## Logging Errors - -```python -import logging -from anthropic import APIError, APIStatusError - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -try: - message = client.messages.create(...) -except APIStatusError as e: - logger.error( - "API request failed", - extra={ - "request_id": e.request_id, - "status_code": e.status_code, - "error_message": e.message, - "error_type": e.body.get("error", {}).get("type") if e.body else None, - } - ) -except APIError as e: - logger.error(f"API error: {e.message}") -``` - -## Graceful Degradation - -```python -from anthropic import APIError - -def get_response_with_fallback(user_message): - """Try primary model, fall back to simpler model on error.""" - models = [ - "claude-sonnet-4-5-20250929", - "claude-3-5-haiku-20241022", - ] - - for model in models: - try: - message = client.messages.create( - model=model, - max_tokens=1024, - messages=[{"role": "user", "content": user_message}] - ) - return message.content[0].text - except APIError as e: - print(f"Failed with {model}: {e.message}") - continue - - return "Service temporarily unavailable" - -response = get_response_with_fallback("What is AI?") -``` - -## Validate Inputs Before API Call - -```python -from anthropic import BadRequestError - -def validate_and_create_message(messages): - """Validate inputs before making API call.""" - # Validate message structure - if not messages: - raise ValueError("Messages list cannot be empty") - - for msg in messages: - if "role" not in msg or "content" not in msg: - raise ValueError("Each message must have 'role' and 'content'") - - if msg["role"] not in ["user", "assistant"]: - raise ValueError(f"Invalid role: {msg['role']}") - - # Make API call - try: - return client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=messages - ) - except BadRequestError as e: - print(f"API validation failed: {e.message}") - raise -``` - -## Best Practices - -### 1. Always Handle Exceptions - -Never let exceptions go unhandled in production. - -### 2. Use Specific Exception Types - -Catch specific exceptions for targeted handling. - -### 3. Implement Retry Logic - -Always retry transient errors (rate limits, server errors). - -### 4. Log with Context - -Include request IDs and relevant context in logs. - -### 5. Set Reasonable Timeouts - -```python -import httpx - -client = Anthropic( - timeout=httpx.Timeout(60.0), - max_retries=3 -) -``` - -### 6. Monitor Error Rates - -Track error rates to detect issues early. - -## See Also - -- [Error Reference](../reference/errors.md) - Complete exception hierarchy -- [Client Configuration](../reference/client-config.md) - Timeout and retry configuration diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md deleted file mode 100644 index 76cc0e2e0..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/getting-started.md +++ /dev/null @@ -1,229 +0,0 @@ -# Getting Started Guide - -Step-by-step guide to using the Anthropic Python SDK. - -## Installation - -```bash -pip install anthropic -``` - -Optional extras: -```bash -pip install anthropic[bedrock] # AWS Bedrock -pip install anthropic[vertex] # Google Vertex AI -pip install anthropic[aiohttp] # Alternative async HTTP -``` - -## Authentication - -Set your API key as an environment variable: - -```bash -export ANTHROPIC_API_KEY='your-api-key' -``` - -Or pass it explicitly: - -```python -from anthropic import Anthropic - -client = Anthropic(api_key="your-api-key") -``` - -## Basic Message - -```python -from anthropic import Anthropic - -client = Anthropic() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[ - {"role": "user", "content": "Hello, Claude!"} - ] -) - -print(message.content[0].text) -``` - -## System Prompts - -Configure Claude's behavior with system prompts: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - system="You are a helpful AI assistant specializing in Python programming.", - messages=[ - {"role": "user", "content": "How do I read a file?"} - ] -) -``` - -## Multi-Turn Conversations - -Maintain conversation history: - -```python -messages = [ - {"role": "user", "content": "My name is Alice."}, - {"role": "assistant", "content": "Hello Alice! Nice to meet you."}, - {"role": "user", "content": "What's my name?"} -] - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=messages -) - -print(message.content[0].text) # "Your name is Alice." -``` - -## Streaming Responses - -Stream responses for real-time feedback: - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[ - {"role": "user", "content": "Write a short story"} - ] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -print() -``` - -## Working with Images - -Send images to Claude: - -```python -import base64 - -with open("image.jpg", "rb") as f: - image_data = base64.standard_b64encode(f.read()).decode() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "image", - "source": { - "type": "base64", - "media_type": "image/jpeg", - "data": image_data - } - }, - {"type": "text", "text": "What's in this image?"} - ] - }] -) -``` - -## Error Handling - -Always handle potential errors: - -```python -from anthropic import APIError, RateLimitError - -try: - message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) -except RateLimitError as e: - print(f"Rate limited. Retry after {e.response.headers.get('retry-after')}s") -except APIError as e: - print(f"API error: {e.message}") -``` - -## Async Usage - -For async applications: - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - message = await client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) - - print(message.content[0].text) - -asyncio.run(main()) -``` - -## Best Practices - -### 1. Use Context Managers - -```python -with Anthropic() as client: - message = client.messages.create(...) -# Client automatically closed -``` - -### 2. Handle Errors Gracefully - -```python -try: - message = client.messages.create(...) -except APIError as e: - # Handle error - ... -``` - -### 3. Use Appropriate Models - -- `claude-sonnet-4-5-20250929` - Balanced intelligence and speed -- `claude-opus-4-5-20250929` - Maximum capability -- `claude-3-5-haiku-20241022` - Fast and cost-effective - -### 4. Set Reasonable Timeouts - -```python -import httpx - -client = Anthropic( - timeout=httpx.Timeout(60.0) -) -``` - -### 5. Track Token Usage - -```python -message = client.messages.create(...) -print(f"Input tokens: {message.usage.input_tokens}") -print(f"Output tokens: {message.usage.output_tokens}") -``` - -## Next Steps - -- [Multimodal Content](./multimodal.md) - Images, documents, PDFs -- [Tool Usage](./tool-usage.md) - Function calling -- [Streaming Guide](./streaming-guide.md) - Advanced streaming -- [Error Handling](./error-handling.md) - Robust error management - -## See Also - -- [Messages API](../api/messages.md) - Complete API reference -- [Client Configuration](../reference/client-config.md) - Advanced configuration diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md deleted file mode 100644 index 7d6f9360a..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/multimodal.md +++ /dev/null @@ -1,300 +0,0 @@ -# Multimodal Content Guide - -Working with images, documents, and PDFs in Claude conversations. - -## Image Input - -### Base64 Images - -```python -import base64 - -with open("image.jpg", "rb") as f: - image_data = base64.standard_b64encode(f.read()).decode() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "image", - "source": { - "type": "base64", - "media_type": "image/jpeg", - "data": image_data - } - }, - {"type": "text", "text": "Describe this image"} - ] - }] -) -``` - -### Supported Image Formats - -```python { .api } -# Supported MIME types -"image/jpeg" -"image/png" -"image/gif" -"image/webp" -``` - -### Multiple Images - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img1}}, - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img2}}, - {"type": "text", "text": "Compare these images"} - ] - }] -) -``` - -## PDF Documents - -### Base64 PDF - -```python -import base64 - -with open("document.pdf", "rb") as f: - pdf_data = base64.standard_b64encode(f.read()).decode() - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "base64", - "media_type": "application/pdf", - "data": pdf_data - } - }, - {"type": "text", "text": "Summarize this document"} - ] - }] -) -``` - -### PDF URL - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "url", - "media_type": "application/pdf", - "url": "https://example.com/document.pdf" - } - }, - {"type": "text", "text": "Summarize this document"} - ] - }] -) -``` - -## Plain Text Documents - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - { - "type": "document", - "source": { - "type": "text", - "media_type": "text/plain", - "data": "Long text content..." - } - }, - {"type": "text", "text": "Analyze this text"} - ] - }] -) -``` - -## Mixed Content - -Combine text, images, and documents: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - {"type": "text", "text": "Review this presentation:"}, - {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": pdf_data}}, - {"type": "text", "text": "Here's the cover image:"}, - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": img_data}}, - {"type": "text", "text": "What are the key points?"} - ] - }] -) -``` - -## Image Analysis Tasks - -### Object Detection - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {...}}, - {"type": "text", "text": "List all objects visible in this image"} - ] - }] -) -``` - -### Text Extraction (OCR) - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "image", "source": {...}}, - {"type": "text", "text": "Extract all text from this image"} - ] - }] -) -``` - -### Image Comparison - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "text", "text": "Before:"}, - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": before_img}}, - {"type": "text", "text": "After:"}, - {"type": "image", "source": {"type": "base64", "media_type": "image/jpeg", "data": after_img}}, - {"type": "text", "text": "What changed?"} - ] - }] -) -``` - -## Document Analysis Tasks - -### Summarization - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {...}}, - {"type": "text", "text": "Provide a brief summary"} - ] - }] -) -``` - -### Q&A on Documents - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {...}}, - {"type": "text", "text": "What is the main conclusion?"} - ] - }] -) -``` - -### Citation Support (Beta) - -```python -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - citations={"type": "enabled"}, - messages=[{ - "role": "user", - "content": [ - {"type": "document", "source": {...}}, - {"type": "text", "text": "Summarize with citations"} - ] - }] -) - -for block in message.content: - if hasattr(block, 'citations'): - for citation in block.citations: - print(f"Citation: {citation.cited_text}") -``` - -## Best Practices - -### 1. Optimize Image Size - -```python -from PIL import Image -import io -import base64 - -def optimize_image(image_path, max_size=(1024, 1024)): - img = Image.open(image_path) - img.thumbnail(max_size, Image.Resampling.LANCZOS) - - buffer = io.BytesIO() - img.save(buffer, format="JPEG", quality=85) - return base64.standard_b64encode(buffer.getvalue()).decode() - -optimized_data = optimize_image("large_image.jpg") -``` - -### 2. Handle Large PDFs - -For very large PDFs, consider splitting or summarizing sections. - -### 3. Specify Context - -Always provide clear instructions about what you want from the content. - -## See Also - -- [Messages API](../api/messages.md) - Content block types -- [Beta Features](../beta/index.md) - Citations feature -- [Getting Started](./getting-started.md) - Basic usage diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md deleted file mode 100644 index 859fd6cf0..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/streaming-guide.md +++ /dev/null @@ -1,221 +0,0 @@ -# Streaming Guide - -Advanced patterns for streaming responses from Claude. - -## Basic Streaming - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -print() -``` - -## Event-Based Streaming - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) as stream: - for event in stream: - if event.type == "message_start": - print("Stream started") - elif event.type == "content_block_start": - print(f"\nContent block {event.index} started") - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - elif event.type == "message_stop": - print("\nStream completed") -``` - -## Get Final Message - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "What is 2+2?"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - -# Get complete message after streaming -message = stream.get_final_message() -print(f"\nTokens used: {message.usage.output_tokens}") -``` - -## Stream with Tool Use - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[{ - "name": "get_weather", - "description": "Get weather", - "input_schema": { - "type": "object", - "properties": {"location": {"type": "string"}}, - "required": ["location"] - } - }], - messages=[{"role": "user", "content": "What's the weather in SF?"}] -) as stream: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) - elif event.delta.type == "input_json_delta": - print(event.delta.partial_json, end="") - -message = stream.get_final_message() -``` - -## Async Streaming - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - async with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a haiku"}] - ) as stream: - async for text in stream.text_stream: - print(text, end="", flush=True) - print() - -asyncio.run(main()) -``` - -## Track Token Usage - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write an essay"}] -) as stream: - tokens_used = 0 - - for event in stream: - if event.type == "message_delta": - tokens_used = event.usage.output_tokens - print(f"\nTokens: {tokens_used}", end="\r") - elif event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) -``` - -## Stream with Beta Features - -```python -with client.beta.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=2048, - thinking={"type": "enabled"}, - messages=[{"role": "user", "content": "Explain quantum computing"}] -) as stream: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "thinking_delta": - print(f"[Thinking: {event.delta.thinking}]") - elif event.delta.type == "text_delta": - print(event.delta.text, end="", flush=True) -``` - -## Error Handling - -```python -from anthropic import APIError - -try: - with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] - ) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -except APIError as e: - print(f"\nStream error: {e.message}") -``` - -## Buffered Streaming - -Buffer output for smoother display: - -```python -import time - -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a story"}] -) as stream: - buffer = "" - - for text in stream.text_stream: - buffer += text - - # Flush buffer every 10 characters or at punctuation - if len(buffer) >= 10 or text in ".!?\n": - print(buffer, end="", flush=True) - buffer = "" - time.sleep(0.01) # Smooth animation - - if buffer: # Flush remaining - print(buffer, end="", flush=True) -``` - -## Best Practices - -### 1. Use Context Managers - -Always use `with` statement to ensure proper cleanup: - -```python -with client.messages.stream(...) as stream: - ... -# Stream automatically closed -``` - -### 2. Handle Interruptions - -```python -import signal - -def signal_handler(sig, frame): - print("\nStream interrupted") - stream.close() - sys.exit(0) - -signal.signal(signal.SIGINT, signal_handler) -``` - -### 3. Set Appropriate Timeouts - -```python -import httpx - -client = Anthropic( - timeout=httpx.Timeout(120.0) # 2 minutes for streaming -) -``` - -## See Also - -- [Streaming API](../api/streaming.md) - Complete API reference -- [Messages API](../api/messages.md) - Message creation -- [Beta Features](../beta/index.md) - Extended thinking streaming diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md b/.tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md deleted file mode 100644 index a1a55a3a0..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/guides/tool-usage.md +++ /dev/null @@ -1,342 +0,0 @@ -# Tool Usage Guide - -Comprehensive guide to using tools (function calling) with Claude for building agentic workflows. - -## Tool Basics - -Tools let Claude call functions you define, enabling it to take actions and retrieve information. - -### Manual Tool Definition - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[{ - "name": "get_weather", - "description": "Get weather for a location", - "input_schema": { - "type": "object", - "properties": { - "location": {"type": "string", "description": "City and state"} - }, - "required": ["location"] - } - }], - messages=[{"role": "user", "content": "What's the weather in SF?"}] -) -``` - -### Using Tool Decorators - -```python -from anthropic import beta_tool - -@beta_tool -def get_weather(location: str, unit: str = "fahrenheit") -> dict: - """ - Get weather for a location. - - Args: - location: City and state, e.g. San Francisco, CA - unit: Temperature unit (celsius or fahrenheit) - """ - # Implementation - return {"temperature": 72, "condition": "sunny", "unit": unit} - -message = client.beta.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather], - messages=[{"role": "user", "content": "What's the weather in NYC?"}] -) -``` - -## Tool Execution Flow - -### Manual Execution - -```python -# 1. Send initial request -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - messages=[{"role": "user", "content": "What's the weather in Paris?"}] -) - -# 2. Extract tool use -tool_use = next(block for block in message.content if block.type == "tool_use") - -# 3. Execute function -result = get_weather(location=tool_use.input["location"]) - -# 4. Send result back -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - messages=[ - {"role": "user", "content": "What's the weather in Paris?"}, - {"role": "assistant", "content": message.content}, - { - "role": "user", - "content": [{ - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": str(result) - }] - } - ] -) -``` - -### Automatic Execution with Tool Runner - -```python -@beta_tool -def get_weather(location: str) -> dict: - """Get weather for location.""" - return {"temp": 72, "condition": "sunny"} - -@beta_tool -def get_time(timezone: str = "UTC") -> str: - """Get current time in timezone.""" - from datetime import datetime - return datetime.now().strftime("%H:%M") - -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather, get_time], - messages=[{"role": "user", "content": "What's the weather and time in SF?"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -## Tool Choice Control - -### Auto (Default) - -Let Claude decide whether to use tools: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "auto"}, - messages=[...] -) -``` - -### Force Any Tool - -Require Claude to use at least one tool: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "any"}, - messages=[...] -) -``` - -### Force Specific Tool - -Require Claude to use a specific tool: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "tool", "name": "get_weather"}, - messages=[...] -) -``` - -### Disable All Tools - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={"type": "none"}, - messages=[...] -) -``` - -### Disable Parallel Tool Use - -Force sequential tool calls: - -```python -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - tool_choice={ - "type": "auto", - "disable_parallel_tool_use": True - }, - messages=[...] -) -``` - -## Advanced Patterns - -### Stateful Tools - -```python -class Calculator: - def __init__(self): - self.memory = 0 - - @beta_tool - def calculate(self, expression: str) -> float: - """Evaluate mathematical expression.""" - result = eval(expression) - self.memory = result - return result - - @beta_tool - def recall(self) -> float: - """Recall last calculation result.""" - return self.memory - -calc = Calculator() - -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[calc.calculate, calc.recall], - messages=[{"role": "user", "content": "Calculate 5*8, then add 10"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -### Error Handling in Tools - -```python -@beta_tool -def divide(a: float, b: float) -> float: - """Divide two numbers.""" - if b == 0: - raise ValueError("Cannot divide by zero") - return a / b - -# In tool result -try: - result = divide(10, 0) - tool_result = str(result) - is_error = False -except Exception as e: - tool_result = str(e) - is_error = True - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[...], - messages=[ - ..., - { - "role": "user", - "content": [{ - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": tool_result, - "is_error": is_error - }] - } - ] -) -``` - -### Async Tools - -```python -from anthropic import beta_async_tool - -@beta_async_tool -async def fetch_data(url: str) -> dict: - """Fetch data from URL.""" - async with httpx.AsyncClient() as client: - response = await client.get(url) - return response.json() - -# Use with async tool runner -async for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[fetch_data], - messages=[...] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) -``` - -## Best Practices - -### 1. Clear Tool Descriptions - -```python -@beta_tool -def search_database( - query: str, - limit: int = 10, - category: str | None = None -) -> list[dict]: - """ - Search database for items matching query. - - Args: - query: Search keywords or phrase - limit: Maximum results to return (1-100) - category: Optional category filter (e.g., "electronics", "books") - - Returns: - List of matching items with id, name, price - """ - ... -``` - -### 2. Validate Tool Inputs - -```python -@beta_tool -def set_temperature(degrees: float, unit: str = "celsius") -> dict: - """Set thermostat temperature.""" - if unit not in ["celsius", "fahrenheit"]: - raise ValueError(f"Invalid unit: {unit}") - if degrees < -50 or degrees > 50: - raise ValueError(f"Temperature out of range: {degrees}") - # Set temperature - return {"status": "success", "temperature": degrees, "unit": unit} -``` - -### 3. Return Structured Data - -```python -@beta_tool -def get_user_info(user_id: str) -> dict: - """Get user information.""" - return { - "id": user_id, - "name": "Alice Smith", - "email": "alice@example.com", - "created_at": "2024-01-15" - } -``` - -## See Also - -- [Tool Use API](../api/tools.md) - Complete API reference -- [Beta Features](../beta/index.md) - Tool runner and advanced features -- [Messages API](../api/messages.md) - Message creation diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/index.md b/.tessl/tiles/tessl/pypi-anthropic/docs/index.md deleted file mode 100644 index 747b2a9cc..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/index.md +++ /dev/null @@ -1,398 +0,0 @@ -# Anthropic Python SDK - -The official Python library for the Anthropic REST API, providing type-safe access to Claude AI models with both sync and async support. - -## Package Information - -- **Package Name**: anthropic -- **Package Type**: Python SDK -- **Language**: Python 3.8+ -- **Installation**: `pip install anthropic` -- **Repository**: https://github.com/anthropics/anthropic-sdk-python -- **License**: MIT - -## Installation - -```bash -pip install anthropic -``` - -Platform-specific extras: -- `pip install anthropic[bedrock]` - AWS Bedrock -- `pip install anthropic[vertex]` - Google Vertex AI -- `pip install anthropic[aiohttp]` - Alternative async client - -## Quick Start - -### Basic Message - -```python { .api } -from anthropic import Anthropic - -client = Anthropic() # Reads ANTHROPIC_API_KEY from environment - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello, Claude"}] -) - -print(message.content[0].text) -``` - -### Async Message - -```python { .api } -from anthropic import AsyncAnthropic - -client = AsyncAnthropic() -message = await client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello"}] -) -``` - -### Stream Response - -```python { .api } -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -## Available Models - -**Claude 4.5 (Latest):** -- `claude-opus-4-5-20250929` - Most capable -- `claude-sonnet-4-5-20250929` - Balanced (recommended) - -**Claude 3.5:** -- `claude-3-5-sonnet-20241022` - Previous Sonnet -- `claude-3-5-haiku-20241022` - Fast and cost-effective - -**[→ Complete model list and selection guide](./api/models.md)** - -## Common Tasks - -Choose based on your use case: - -**[Basic Messaging](./common-tasks/basic-messaging.md)** -- Simple text conversations -- System prompts -- Multi-turn conversations -- Temperature control - -**[Multimodal Input](./common-tasks/multimodal-input.md)** -- Image analysis (JPG, PNG, GIF, WebP) -- PDF document processing -- Mixed content (text + images + documents) - -**[Tool Integration](./common-tasks/tool-integration.md)** -- Function calling basics -- Auto-execution with tool_runner -- Async tools -- Error handling in tools - -**[Streaming Responses](./common-tasks/streaming-responses.md)** -- Real-time text streaming -- Event-based processing -- Token usage tracking -- Error handling - -**[Batch Processing](./guides/batch-processing.md)** -- Process thousands of requests -- 50% cost reduction -- High-throughput scenarios - -## API Quick Reference - -Fast lookup for method signatures and parameters: - -**[Messages API](./quick-reference/messages.md)** - Core message creation -- `create()` - Create single message -- `stream()` - Stream message response -- `count_tokens()` - Estimate token usage - -**[Streaming API](./quick-reference/streaming.md)** - Real-time response processing -- `stream()` - Context manager for streaming -- Event types and handling -- Helper methods - -**[Tools API](./quick-reference/tools.md)** - Function calling -- `@beta_tool` decorator -- `tool_runner()` - Auto-execution -- Manual tool handling - -**[Batches API](./quick-reference/batches.md)** - Async batch processing -- `create()` - Submit batch -- `retrieve()` - Check status -- `results()` - Get outputs - -**[Models API](./quick-reference/models.md)** - Model information -- `retrieve()` - Get model details -- `list()` - Browse available models - -## Detailed API Documentation - -In-depth reference with all parameters, types, and examples: - -### Core APIs - -- **[Messages API](./api/messages.md)** - Complete messages API reference with all parameters, types, and examples -- **[Streaming API](./api/streaming.md)** - Detailed streaming architecture, events, and patterns -- **[Tools API](./api/tools.md)** - Function calling with decorators and manual definitions -- **[Batches API](./api/batches.md)** - Batch processing for high-throughput use cases -- **[Models API](./api/models.md)** - Model information and selection -- **[Completions API](./api/completions.md)** - Legacy text completions API (deprecated, use Messages API instead) - -### Implementation Guides - -- **[Getting Started](./guides/getting-started.md)** - Installation, authentication, and first steps -- **[Multimodal Content](./guides/multimodal.md)** - Working with images, documents, and PDFs -- **[Tool Usage](./guides/tool-usage.md)** - Building agentic workflows with function calling -- **[Streaming Guide](./guides/streaming-guide.md)** - Advanced streaming patterns and best practices -- **[Batch Processing](./guides/batch-processing.md)** - Large-scale async message processing -- **[Error Handling](./guides/error-handling.md)** - Robust error management and retry strategies - -### Platform Integrations - -- **[AWS Bedrock](./platforms/bedrock.md)** - Use Claude on AWS infrastructure -- **[Google Vertex AI](./platforms/vertex.md)** - Use Claude on GCP infrastructure -- **[Azure AI Foundry](./platforms/foundry.md)** - Use Claude on Azure infrastructure - -### Reference Documentation - -- **[Client Configuration](./reference/client-config.md)** - Client initialization, timeouts, retries, HTTP options -- **[Type System](./reference/types.md)** - Complete Pydantic type definitions -- **[Error Handling](./reference/errors.md)** - Exception hierarchy and error types -- **[Pagination](./reference/pagination.md)** - List operation pagination patterns -- **[Utilities](./reference/utilities.md)** - Helper functions and utilities - -## Beta Features - -Access experimental capabilities via `client.beta` namespace: - -**[Beta Overview](./beta/index.md)** - Introduction to beta features - -**Message Enhancement Features:** -- **[Extended Thinking](./beta/message-features.md#extended-thinking)** - Long-form reasoning with budget control -- **[Citations](./beta/message-features.md#citations)** - Source attribution for document-based responses -- **[Web Search](./beta/message-features.md#web-search)** - Real-time web information retrieval -- **[Code Execution](./beta/message-features.md#code-execution)** - Python sandbox execution -- **[Computer Use](./beta/message-features.md#computer-use)** - GUI interaction capabilities -- **[MCP Integration](./beta/message-features.md#mcp-integration)** - Model Context Protocol tools - -**[→ All Message Features](./beta/message-features.md)** - -**Resource Management:** -- **[Skills API](./beta/skills.md)** - Create and manage reusable capabilities -- **[Files API](./beta/files.md)** - Upload and manage document files -- **[Beta Batches](./beta/batches.md)** - Batch processing with beta features - -## Client Configuration - -### Basic Setup - -```python { .api } -from anthropic import Anthropic - -# Environment variable (recommended) -client = Anthropic() # Uses ANTHROPIC_API_KEY - -# Explicit API key -client = Anthropic(api_key="your-api-key") - -# Context manager (automatic cleanup) -with Anthropic() as client: - message = client.messages.create(...) -``` - -### Common Configurations - -```python { .api } -import httpx - -# Custom timeout -client = Anthropic(timeout=120.0) - -# Granular timeout -client = Anthropic( - timeout=httpx.Timeout( - connect=10.0, - read=60.0, - write=10.0, - pool=10.0 - ) -) - -# Retry configuration -client = Anthropic(max_retries=5) - -# Custom headers -client = Anthropic( - default_headers={"X-Custom": "value"} -) -``` - -**[→ Complete configuration reference](./reference/client-config.md)** - -## Error Handling - -### Basic Pattern - -```python -from anthropic import APIError, RateLimitError - -try: - message = client.messages.create(...) -except RateLimitError as e: - retry_after = e.response.headers.get("retry-after") - print(f"Rate limited. Retry after {retry_after}s") -except APIError as e: - print(f"API error: {e.message}") -``` - -### Exception Hierarchy - -```python { .api } -AnthropicError -├── APIError -│ ├── APIStatusError -│ │ ├── BadRequestError (400) -│ │ ├── AuthenticationError (401) -│ │ ├── PermissionDeniedError (403) -│ │ ├── NotFoundError (404) -│ │ ├── RateLimitError (429) -│ │ ├── InternalServerError (≥500) -│ ├── APIConnectionError -│ ├── APITimeoutError -│ └── APIResponseValidationError -``` - -**[→ Complete error reference and retry patterns](./reference/errors.md)** - -**[→ Error handling guide with advanced patterns](./guides/error-handling.md)** - -## Environment Variables - -- `ANTHROPIC_API_KEY` - API key for authentication (required) -- `ANTHROPIC_BASE_URL` - Override base URL (optional) -- `ANTHROPIC_AUTH_TOKEN` - Bearer token alternative (optional) - -Platform-specific variables documented in platform guides. - -## SDK Architecture - -### Client Hierarchy - -- **Anthropic / AsyncAnthropic** - Main clients for direct API access -- **AnthropicBedrock / AsyncAnthropicBedrock** - AWS Bedrock integration -- **AnthropicVertex / AsyncAnthropicVertex** - Google Vertex AI integration -- **AnthropicFoundry / AsyncAnthropicFoundry** - Azure AI Foundry integration - -### Resource Structure - -```python { .api } -client.messages # Messages resource - .create() # Create message - .stream() # Stream message - .count_tokens() # Count tokens - .batches # Batches sub-resource - .create() # Create batch - .retrieve() # Get batch status - .list() # List batches - .cancel() # Cancel batch - .delete() # Delete batch - .results() # Get batch results - -client.beta # Beta features namespace - .messages # Beta messages with additional features - .create() # Create with beta features - .stream() # Stream with beta features - .tool_runner() # Auto-execute tools - .skills # Skills management - .files # File management - -client.models # Models information - .retrieve() # Get model info - .list() # List models -``` - -## Type System - -All requests and responses use Pydantic models for type safety: - -```python { .api } -class Message(BaseModel): - id: str - type: Literal["message"] - role: Literal["assistant"] - content: list[ContentBlock] - model: str - stop_reason: StopReason | None - usage: Usage - -ContentBlock = Union[TextBlock, ToolUseBlock] -StopReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] -``` - -**[→ Complete type definitions](./reference/types.md)** - -## Decision Guide for Common Scenarios - -### "I need to send a message to Claude" -→ **[Basic Messaging](./common-tasks/basic-messaging.md)** or **[Messages API](./api/messages.md)** - -### "I need to process images or PDFs" -→ **[Multimodal Input](./common-tasks/multimodal-input.md)** or **[Multimodal Guide](./guides/multimodal.md)** - -### "I need Claude to call functions/use tools" -→ **[Tool Integration](./common-tasks/tool-integration.md)** or **[Tools API](./api/tools.md)** - -### "I need real-time streaming output" -→ **[Streaming Responses](./common-tasks/streaming-responses.md)** or **[Streaming API](./api/streaming.md)** - -### "I need to process thousands of messages" -→ **[Batch Processing Guide](./guides/batch-processing.md)** or **[Batches API](./api/batches.md)** - -### "I'm getting errors" -→ **[Error Reference](./reference/errors.md)** or **[Error Handling Guide](./guides/error-handling.md)** - -### "I need extended reasoning/thinking" -→ **[Beta Overview](./beta/index.md)** → **[Extended Thinking](./beta/message-features.md#extended-thinking)** - -### "I need web search or code execution" -→ **[Beta Overview](./beta/index.md)** → **[Message Features](./beta/message-features.md)** - -### "I'm using AWS/GCP/Azure" -→ **[Platform Integrations](#platform-integrations)** → Choose your platform - -## Package Constants - -```python { .api } -# Client Configuration Constants -DEFAULT_TIMEOUT: float = 600.0 # 10 minutes default timeout for requests -DEFAULT_MAX_RETRIES: int = 2 # Default number of retry attempts -DEFAULT_CONNECTION_LIMITS: httpx.Limits # Default HTTP connection pool limits - -# Legacy Text Completion Prompt Constants -HUMAN_PROMPT: str = "\n\nHuman:" # Legacy prompt marker for human messages -AI_PROMPT: str = "\n\nAssistant:" # Legacy prompt marker for AI responses - -# Sentinel Values -NOT_GIVEN: NotGiven # Sentinel indicating parameter not provided -``` - -**Note**: `HUMAN_PROMPT` and `AI_PROMPT` are legacy constants for the deprecated Text Completions API. Use the Messages API instead for new applications. - -## Support Resources - -- **API Documentation**: https://docs.anthropic.com -- **SDK Repository**: https://github.com/anthropics/anthropic-sdk-python -- **Model Pricing**: https://anthropic.com/pricing -- **Rate Limits**: https://docs.anthropic.com/rate-limits diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md deleted file mode 100644 index c08df71b7..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/bedrock.md +++ /dev/null @@ -1,148 +0,0 @@ -# AWS Bedrock Integration - -Use Claude models via AWS Bedrock with automatic authentication and region configuration. - -## Installation - -```bash -pip install anthropic[bedrock] -``` - -## Client Initialization - -```python { .api } -class AnthropicBedrock: - """Synchronous client for Claude on AWS Bedrock.""" - - def __init__( - self, - *, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_session_token: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: dict[str, str] | None = None, - http_client: httpx.Client | None = None, - ): - """ - Initialize Bedrock client. - - Parameters: - aws_access_key: AWS access key ID (or AWS_ACCESS_KEY_ID env var) - aws_secret_key: AWS secret access key (or AWS_SECRET_ACCESS_KEY env var) - aws_session_token: AWS session token (or AWS_SESSION_TOKEN env var) - aws_region: AWS region (or AWS_REGION env var, default: us-east-1) - aws_profile: AWS profile from ~/.aws/credentials - timeout: Request timeout - max_retries: Maximum retry attempts - default_headers: Custom headers - http_client: Custom httpx.Client - """ - ... - -class AsyncAnthropicBedrock: - """Asynchronous client for Claude on AWS Bedrock.""" - # Same parameters as AnthropicBedrock - ... -``` - -## Bedrock Model Identifiers - -```python { .api } -# Claude 3.5 on Bedrock -"anthropic.claude-3-5-sonnet-20241022-v2:0" -"anthropic.claude-3-5-sonnet-20240620-v1:0" -"anthropic.claude-3-5-haiku-20241022-v1:0" - -# Claude 3 on Bedrock -"anthropic.claude-3-opus-20240229-v1:0" -"anthropic.claude-3-sonnet-20240229-v1:0" -"anthropic.claude-3-haiku-20240307-v1:0" -``` - -## Quick Examples - -### Basic Usage - -```python -from anthropic import AnthropicBedrock - -client = AnthropicBedrock() # Uses environment variables - -message = client.messages.create( - model="anthropic.claude-3-5-sonnet-20241022-v2:0", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello!"}] -) -``` - -### Explicit Credentials - -```python -client = AnthropicBedrock( - aws_access_key="AKIA...", - aws_secret_key="wJal...", - aws_region="us-east-1" -) -``` - -### Using AWS Profile - -```python -client = AnthropicBedrock( - aws_profile="production", - aws_region="us-west-2" -) -``` - -### Streaming - -```python -with client.messages.stream( - model="anthropic.claude-3-5-sonnet-20241022-v2:0", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -### Multi-Region Deployment - -```python -regions = ["us-east-1", "us-west-2", "eu-west-1"] - -for region in regions: - client = AnthropicBedrock(aws_region=region) - message = client.messages.create(...) - client.close() -``` - -### Async Client - -```python -import asyncio -from anthropic import AsyncAnthropicBedrock - -async def main(): - client = AsyncAnthropicBedrock(aws_region="us-east-1") - message = await client.messages.create(...) - await client.close() - -asyncio.run(main()) -``` - -## Environment Variables - -- `AWS_ACCESS_KEY_ID` - AWS access key -- `AWS_SECRET_ACCESS_KEY` - AWS secret key -- `AWS_SESSION_TOKEN` - AWS session token -- `AWS_REGION` - AWS region (default: us-east-1) - -## See Also - -- [Messages API](../api/messages.md) - Core message creation -- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md deleted file mode 100644 index 6868c08bc..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/foundry.md +++ /dev/null @@ -1,144 +0,0 @@ -# Azure AI Foundry Integration - -Access Claude models via Azure AI Foundry with API key or Azure AD authentication. - -## Overview - -Azure AI Foundry provides access to Claude through Microsoft's Azure AI platform with: -- Azure API key authentication -- Azure Active Directory (AAD) token provider authentication -- Automatic base URL construction from resource names - -## Client Initialization - -```python { .api } -class AnthropicFoundry: - def __init__( - self, - *, - resource: str | None = None, - api_key: str | None = None, - azure_ad_token_provider: Callable[[], str] | None = None, - base_url: str | None = None, - timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: dict[str, str] | None = None, - http_client: httpx.Client | None = None, - ): - """ - Initialize Azure AI Foundry client. - - Parameters: - resource: Azure resource name (e.g., "my-resource" for - https://my-resource.services.ai.azure.com/anthropic/) - api_key: Azure API key (or ANTHROPIC_FOUNDRY_API_KEY env var) - azure_ad_token_provider: Function returning Azure AD token - base_url: Full base URL (mutually exclusive with resource) - timeout: Request timeout - max_retries: Maximum retry attempts - default_headers: Custom headers - http_client: Custom httpx.Client - - Environment Variables: - ANTHROPIC_FOUNDRY_API_KEY: Default API key - ANTHROPIC_FOUNDRY_RESOURCE: Default resource name - ANTHROPIC_FOUNDRY_BASE_URL: Default base URL - - Note: - Must provide either api_key or azure_ad_token_provider. - Must provide either resource or base_url. - """ - ... - -class AsyncAnthropicFoundry: - # Same parameters, but azure_ad_token_provider can return Awaitable[str] - ... -``` - -## Quick Examples - -### API Key Authentication - -```python -from anthropic import AnthropicFoundry - -client = AnthropicFoundry( - resource="my-resource", - api_key="your-api-key" -) - -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello!"}] -) -``` - -### Azure AD Token Provider - -```python -def get_azure_ad_token(): - """Fetch Azure AD token using your preferred method.""" - # Implement token retrieval - return "your-azure-ad-token" - -client = AnthropicFoundry( - resource="my-resource", - azure_ad_token_provider=get_azure_ad_token -) -``` - -### Using Base URL Directly - -```python -client = AnthropicFoundry( - base_url="https://my-resource.services.ai.azure.com/anthropic/", - api_key="your-api-key" -) -``` - -### Async Client - -```python -import asyncio -from anthropic import AsyncAnthropicFoundry - -async def main(): - client = AsyncAnthropicFoundry( - resource="my-resource", - api_key="your-api-key" - ) - message = await client.messages.create(...) - -asyncio.run(main()) -``` - -### Streaming - -```python -with client.messages.stream( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a haiku"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -## Limitations - -The Azure AI Foundry integration has the following limitations: -1. **No Models API**: `client.models` resource not available -2. **No Message Batches**: `client.messages.batches` resource not available -3. **No Beta Batches**: `client.beta.messages.batches` resource not available - -## Environment Variables - -- `ANTHROPIC_FOUNDRY_API_KEY` - Default API key -- `ANTHROPIC_FOUNDRY_RESOURCE` - Default resource name -- `ANTHROPIC_FOUNDRY_BASE_URL` - Default base URL - -## See Also - -- [Messages API](../api/messages.md) - Core message creation -- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md b/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md deleted file mode 100644 index 3756993eb..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/platforms/vertex.md +++ /dev/null @@ -1,157 +0,0 @@ -# Google Vertex AI Integration - -Access Claude models on Google Cloud Platform with Vertex AI integration. - -## Installation - -```bash -pip install anthropic[vertex] -``` - -## Client Initialization - -```python { .api } -class AnthropicVertex: - """Synchronous client for Claude on Google Vertex AI.""" - - def __init__( - self, - *, - project_id: str | None = None, - region: str | None = None, - timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: dict[str, str] | None = None, - http_client: httpx.Client | None = None, - ): - """ - Initialize Vertex AI client. - - Parameters: - project_id: GCP project ID (or CLOUD_ML_PROJECT_ID/GOOGLE_CLOUD_PROJECT env var) - region: GCP region (or CLOUD_ML_REGION env var, default: us-east5) - timeout: Request timeout - max_retries: Maximum retry attempts - default_headers: Custom headers - http_client: Custom httpx.Client - """ - ... - -class AsyncAnthropicVertex: - """Asynchronous client for Claude on Google Vertex AI.""" - # Same parameters as AnthropicVertex - ... -``` - -## Vertex Model Identifiers - -```python { .api } -# Claude models on Vertex AI -"claude-3-5-sonnet-v2@20241022" -"claude-3-5-sonnet@20240620" -"claude-3-5-haiku@20241022" -"claude-3-opus@20240229" -"claude-3-sonnet@20240229" -"claude-3-haiku@20240307" -``` - -## Quick Examples - -### Basic Usage - -```python -from anthropic import AnthropicVertex - -client = AnthropicVertex( - project_id="my-gcp-project", - region="us-east5" -) - -message = client.messages.create( - model="claude-3-5-sonnet-v2@20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello!"}] -) -``` - -### Using Environment Variables - -```python -# Set: CLOUD_ML_PROJECT_ID=my-gcp-project -# Set: CLOUD_ML_REGION=us-east5 - -client = AnthropicVertex() # Automatically uses env vars -``` - -### Using Application Default Credentials - -```python -# First authenticate: gcloud auth application-default login - -client = AnthropicVertex( - project_id="my-gcp-project", - region="us-east5" -) -``` - -### Streaming - -```python -with client.messages.stream( - model="claude-3-5-sonnet-v2@20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a story"}] -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -### Multi-Region Deployment - -```python -regions = ["us-east5", "europe-west1", "asia-southeast1"] - -for region in regions: - client = AnthropicVertex( - project_id="my-gcp-project", - region=region - ) - message = client.messages.create(...) - client.close() -``` - -### Async Client - -```python -import asyncio -from anthropic import AsyncAnthropicVertex - -async def main(): - client = AsyncAnthropicVertex( - project_id="my-gcp-project", - region="us-east5" - ) - message = await client.messages.create(...) - await client.close() - -asyncio.run(main()) -``` - -## Environment Variables - -- `CLOUD_ML_PROJECT_ID` or `GOOGLE_CLOUD_PROJECT` - GCP project ID -- `CLOUD_ML_REGION` - GCP region (default: us-east5) -- `GOOGLE_APPLICATION_CREDENTIALS` - Path to service account key JSON - -## Available Regions - -- `us-east5` -- `us-central1` -- `europe-west1` -- `europe-west4` -- `asia-southeast1` - -## See Also - -- [Messages API](../api/messages.md) - Core message creation -- [Streaming API](../api/streaming.md) - Streaming responses diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md deleted file mode 100644 index 3e9dfd47b..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/batches.md +++ /dev/null @@ -1,157 +0,0 @@ -# Batches API - Quick Reference - -Compact API signatures for batch processing. For examples, see **[Batches API Reference](../api/batches.md)**. - -## create() - -```python { .api } -def create( - self, - *, - requests: list[MessageBatchIndividualRequest], # Required: Batch requests - **kwargs -) -> MessageBatch -``` - -**Async:** `async def create(...) -> MessageBatch` - -## retrieve() - -```python { .api } -def retrieve( - self, - message_batch_id: str, # Required: Batch ID - **kwargs -) -> MessageBatch -``` - -**Async:** `async def retrieve(...) -> MessageBatch` - -## list() - -```python { .api } -def list( - self, - *, - before_id: str = NOT_GIVEN, - after_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - **kwargs -) -> SyncPage[MessageBatch] -``` - -**Async:** `async def list(...) -> AsyncPage[MessageBatch]` - -## cancel() - -```python { .api } -def cancel( - self, - message_batch_id: str, # Required: Batch ID to cancel - **kwargs -) -> MessageBatch -``` - -**Async:** `async def cancel(...) -> MessageBatch` - -## delete() - -```python { .api } -def delete( - self, - message_batch_id: str, # Required: Batch ID to delete - **kwargs -) -> DeletedMessageBatch -``` - -**Async:** `async def delete(...) -> DeletedMessageBatch` - -## results() - -```python { .api } -def results( - self, - message_batch_id: str, # Required: Batch ID - **kwargs -) -> JSONLDecoder[MessageBatchIndividualResponse]: - """Stream batch results as JSONL""" - ... -``` - -**Async:** `async def results(...) -> AsyncJSONLDecoder[MessageBatchIndividualResponse]` - -## Key Types - -```python { .api } -class MessageBatchIndividualRequest(TypedDict): - custom_id: str # Your request identifier - params: MessageCreateParams # Same as messages.create() params - -class MessageBatch(BaseModel): - id: str - type: Literal["message_batch"] - processing_status: Literal["in_progress", "canceling", "ended"] - request_counts: MessageBatchRequestCounts - ended_at: str | None - created_at: str - expires_at: str - cancel_initiated_at: str | None - results_url: str | None - -class MessageBatchRequestCounts(BaseModel): - processing: int - succeeded: int - errored: int - canceled: int - expired: int - -class MessageBatchIndividualResponse(BaseModel): - custom_id: str - result: MessageBatchSucceededResult | MessageBatchErroredResult - -class MessageBatchSucceededResult(BaseModel): - type: Literal["succeeded"] - message: Message - -class MessageBatchErroredResult(BaseModel): - type: Literal["errored"] - error: ErrorObject -``` - -## Common Patterns - -```python -# Create batch -batch = client.messages.batches.create( - requests=[ - { - "custom_id": "req-1", - "params": { - "model": "claude-sonnet-4-5-20250929", - "max_tokens": 1024, - "messages": [{"role": "user", "content": "Hello"}] - } - } - ] -) - -# Poll until complete -import time -while True: - batch = client.messages.batches.retrieve(batch.id) - if batch.processing_status == "ended": - break - time.sleep(60) - -# Process results -for response in client.messages.batches.results(batch.id): - if response.result.type == "succeeded": - print(response.result.message.content[0].text) - elif response.result.type == "errored": - print(f"Error: {response.result.error.message}") -``` - -## See Also - -- **[Complete Batches Documentation](../api/batches.md)** - Full details and examples -- **[Batch Processing Guide](../guides/batch-processing.md)** - Advanced patterns and best practices diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md deleted file mode 100644 index 57f4c2e25..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/messages.md +++ /dev/null @@ -1,97 +0,0 @@ -# Messages API - Quick Reference - -Compact API signatures for the Messages API. For examples and detailed documentation, see **[Messages API Reference](../api/messages.md)**. - -## create() - -```python { .api } -def create( - self, - *, - model: str, # Required: Model ID - messages: list[MessageParam], # Required: Conversation messages - max_tokens: int, # Required: Max generation tokens - system: str | list[TextBlockParam] = NOT_GIVEN, # System prompt - metadata: MetadataParam = NOT_GIVEN, # Request metadata (user_id) - stop_sequences: list[str] = NOT_GIVEN, # Custom stop sequences (max 4) - stream: bool = False, # Enable streaming (use stream() instead) - temperature: float = NOT_GIVEN, # Sampling temperature 0.0-1.0 - top_p: float = NOT_GIVEN, # Nucleus sampling 0.0-1.0 - top_k: int = NOT_GIVEN, # Top-k sampling - tools: list[ToolParam] = NOT_GIVEN, # Available tools - tool_choice: ToolChoice = NOT_GIVEN, # Tool selection control - service_tier: Literal["auto", "standard_only"] = NOT_GIVEN, # Service tier - thinking: ThinkingConfigParam = NOT_GIVEN, # Extended thinking (beta) -) -> Message -``` - -**Async:** `async def create(...) -> Message` - -**Raises:** `BadRequestError`, `AuthenticationError`, `RateLimitError`, `InternalServerError` - -## stream() - -```python { .api } -def stream( - self, - *, - model: str, # Required: Model ID - messages: list[MessageParam], # Required: Conversation messages - max_tokens: int, # Required: Max generation tokens - **kwargs # All create() parameters supported -) -> MessageStreamManager -``` - -**Returns:** Context manager with `.text_stream`, `.get_final_message()`, `.get_final_text()` - -**Async:** `async def stream(...) -> AsyncMessageStreamManager` - -## count_tokens() - -```python { .api } -def count_tokens( - self, - *, - model: str, # Required: Model ID - messages: list[MessageParam], # Required: Messages to count - system: str | list[TextBlockParam] = NOT_GIVEN, # System prompt - tools: list[ToolParam] = NOT_GIVEN, # Tools to include - tool_choice: ToolChoice = NOT_GIVEN, # Tool choice config - thinking: ThinkingConfigParam = NOT_GIVEN, # Thinking config -) -> MessageTokensCount -``` - -**Returns:** `MessageTokensCount` with `.input_tokens` - -**Async:** `async def count_tokens(...) -> MessageTokensCount` - -## Key Types - -```python { .api } -class MessageParam(TypedDict): - role: Literal["user", "assistant"] - content: str | list[ContentBlockParam] - -class Message(BaseModel): - id: str - type: Literal["message"] - role: Literal["assistant"] - content: list[ContentBlock] # TextBlock | ToolUseBlock - model: str - stop_reason: StopReason | None - usage: Usage - -class Usage(BaseModel): - input_tokens: int - output_tokens: int - cache_creation_input_tokens: int | None - cache_read_input_tokens: int | None - -StopReason = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] -``` - -## See Also - -- **[Complete API Documentation](../api/messages.md)** - Full details with examples -- **[Basic Messaging Tasks](../common-tasks/basic-messaging.md)** - Task-oriented guide -- **[Type System Reference](../reference/types.md)** - Complete type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md deleted file mode 100644 index 2c27bdf84..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/models.md +++ /dev/null @@ -1,98 +0,0 @@ -# Models API - Quick Reference - -Compact API signatures for model information. For examples, see **[Models API Reference](../api/models.md)**. - -## retrieve() - -```python { .api } -def retrieve( - self, - model_id: str, # Required: Model identifier - **kwargs -) -> ModelInfo -``` - -**Async:** `async def retrieve(...) -> ModelInfo` - -## list() - -```python { .api } -def list( - self, - *, - before_id: str = NOT_GIVEN, - after_id: str = NOT_GIVEN, - limit: int = NOT_GIVEN, - **kwargs -) -> SyncPage[ModelInfo] -``` - -**Async:** `async def list(...) -> AsyncPage[ModelInfo]` - -## Response Type - -```python { .api } -class ModelInfo(BaseModel): - id: str # Model identifier - type: Literal["model"] - display_name: str # Human-readable name - created_at: str # ISO 8601 timestamp -``` - -## Available Model IDs - -```python { .api } -# Claude 4.5 (Latest) -"claude-opus-4-5-20250929" # Most capable model -"claude-sonnet-4-5-20250929" # Balanced intelligence and speed - -# Claude 3.5 -"claude-3-5-sonnet-20241022" # Previous Sonnet version -"claude-3-5-sonnet-20240620" # Earlier Sonnet version -"claude-3-5-haiku-20241022" # Fast, cost-effective - -# Claude 3 -"claude-3-opus-20240229" # Powerful, intelligent -"claude-3-sonnet-20240229" # Balanced -"claude-3-haiku-20240307" # Fast and efficient - -# Legacy (Claude 2) -"claude-2.1" -"claude-2.0" -"claude-instant-1.2" -``` - -## Common Patterns - -```python -# Get specific model info -model = client.models.retrieve("claude-sonnet-4-5-20250929") -print(f"{model.display_name} created: {model.created_at}") - -# List all models -for model in client.models.list(): - print(f"{model.id}: {model.display_name}") - -# Check if model exists -from anthropic import NotFoundError - -def model_exists(model_id: str) -> bool: - try: - client.models.retrieve(model_id) - return True - except NotFoundError: - return False -``` - -## Model Selection Guide - -**Choose based on requirements:** - -- **claude-opus-4-5-20250929** - Complex tasks requiring maximum capability and reasoning -- **claude-sonnet-4-5-20250929** - Balanced performance for most use cases (recommended) -- **claude-3-5-haiku-20241022** - Fast responses and cost-effective for simple tasks -- **claude-3-5-sonnet-20241022** - Previous Sonnet version for compatibility - -## See Also - -- **[Complete Models Documentation](../api/models.md)** - Full details and selection examples diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md deleted file mode 100644 index 568c3325b..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/streaming.md +++ /dev/null @@ -1,115 +0,0 @@ -# Streaming API - Quick Reference - -Compact API signatures for streaming. For examples and patterns, see **[Streaming API Reference](../api/streaming.md)**. - -## stream() - -```python { .api } -def stream( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - **kwargs # All messages.create() parameters supported -) -> MessageStreamManager -``` - -**Async:** `async def stream(...) -> AsyncMessageStreamManager` - -## MessageStreamManager - -```python { .api } -class MessageStreamManager: - def __enter__(self) -> MessageStream - def __exit__(self, exc_type, exc_val, exc_tb) -> None -``` - -## MessageStream - -```python { .api } -class MessageStream: - def __iter__(self) -> Iterator[MessageStreamEvent] - - @property - def text_stream(self) -> Iterator[str]: - """Iterate over text deltas only""" - ... - - @property - def current_message_snapshot(self) -> Message: - """Current accumulated message during streaming""" - ... - - def get_final_message(self) -> Message: - """Complete message after stream ends""" - ... - - def get_final_text(self) -> str: - """Accumulated text after stream ends""" - ... -``` - -**Async:** `AsyncMessageStream` with `async` versions of all methods - -## Stream Events - -```python { .api } -MessageStreamEvent = Union[ - MessageStartEvent, # Stream started - MessageDeltaEvent, # Usage/stop_reason updated - MessageStopEvent, # Stream completed - ContentBlockStartEvent, # New content block - ContentBlockDeltaEvent, # Content delta - ContentBlockStopEvent, # Block completed -] - -class MessageStartEvent(BaseModel): - type: Literal["message_start"] - message: Message # Initial skeleton - -class ContentBlockDeltaEvent(BaseModel): - type: Literal["content_block_delta"] - index: int - delta: ContentBlockDelta # TextDelta | InputJSONDelta - -class TextDelta(BaseModel): - type: Literal["text_delta"] - text: str # Incremental text to append - -class MessageDeltaEvent(BaseModel): - type: Literal["message_delta"] - delta: MessageDelta # stop_reason, stop_sequence - usage: MessageDeltaUsage # output_tokens - -class MessageStopEvent(BaseModel): - type: Literal["message_stop"] -``` - -## Common Patterns - -```python -# Text streaming -with client.messages.stream(...) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) - -# Event processing -with client.messages.stream(...) as stream: - for event in stream: - if event.type == "content_block_delta": - if event.delta.type == "text_delta": - print(event.delta.text, end="") - -# Get final message -with client.messages.stream(...) as stream: - for text in stream.text_stream: - print(text, end="") -message = stream.get_final_message() -``` - -## See Also - -- **[Complete Streaming Documentation](../api/streaming.md)** - All event types and patterns -- **[Streaming Tasks Guide](../common-tasks/streaming-responses.md)** - Task-oriented examples -- **[Streaming Guide](../guides/streaming-guide.md)** - Advanced patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md b/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md deleted file mode 100644 index afc548415..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/quick-reference/tools.md +++ /dev/null @@ -1,159 +0,0 @@ -# Tools API - Quick Reference - -Compact API signatures for tool use (function calling). For examples, see **[Tools API Reference](../api/tools.md)**. - -## Decorators - -```python { .api } -def beta_tool(func: Callable) -> BetaFunctionTool: - """ - Convert sync function to tool with auto-generated schema. - Function must have type hints and docstring. - """ - ... - -def beta_async_tool(func: Callable) -> BetaAsyncFunctionTool: - """ - Convert async function to tool with auto-generated schema. - Function must have type hints and docstring. - """ - ... -``` - -## Tool Runner - -```python { .api } -def tool_runner( - self, - *, - model: str, - messages: list[MessageParam], - max_tokens: int, - tools: list[ToolParam | BetaFunctionTool], - tool_choice: ToolChoice = NOT_GIVEN, - **kwargs # Additional messages.create() parameters -) -> Iterator[BetaMessage]: - """ - Auto-execute tools in agentic loop. - Yields message after each turn until stop_reason == "end_turn". - """ - ... -``` - -**Async:** `async def tool_runner(...) -> AsyncIterator[BetaMessage]` - -## Tool Types - -```python { .api } -class ToolParam(TypedDict): - name: str # Tool identifier - description: str # Tool purpose (for Claude) - input_schema: dict[str, Any] # JSON Schema (draft 2020-12) - cache_control: NotRequired[CacheControlEphemeral] - -ToolChoice = Union[ - ToolChoiceAuto, # Let Claude decide (default) - ToolChoiceAny, # Force Claude to use any tool - ToolChoiceNone, # Disable tools - ToolChoiceTool, # Force specific tool -] - -class ToolChoiceAuto(TypedDict): - type: Literal["auto"] - disable_parallel_tool_use: NotRequired[bool] - -class ToolChoiceAny(TypedDict): - type: Literal["any"] - disable_parallel_tool_use: NotRequired[bool] - -class ToolChoiceTool(TypedDict): - type: Literal["tool"] - name: str # Tool name to force - disable_parallel_tool_use: NotRequired[bool] -``` - -## Response Types - -```python { .api } -class ToolUseBlock(BaseModel): - """Tool invocation in assistant response""" - type: Literal["tool_use"] - id: str # Use in tool_result - name: str # Tool name - input: dict[str, Any] # Tool parameters - -class ToolResultBlockParam(TypedDict): - """Tool result in user message""" - type: Literal["tool_result"] - tool_use_id: str # ID from ToolUseBlock - content: NotRequired[str | list[TextBlockParam | ImageBlockParam]] - is_error: NotRequired[bool] # True if tool execution failed - cache_control: NotRequired[CacheControlEphemeral] -``` - -## Function Tool Classes - -```python { .api } -class BetaFunctionTool: - name: str - description: str - input_schema: dict[str, Any] - func: Callable - - def __call__(self, **kwargs) -> Any: ... - def to_param(self) -> ToolParam: ... - -class BetaAsyncFunctionTool: - name: str - description: str - input_schema: dict[str, Any] - func: Callable - - async def __call__(self, **kwargs) -> Any: ... - def to_param(self) -> ToolParam: ... -``` - -## Common Patterns - -```python -# Decorator usage -@beta_tool -def get_weather(location: str, unit: str = "fahrenheit") -> dict: - """Get weather for location. - - Args: - location: City and state - unit: Temperature unit - """ - return {"temp": 72, "unit": unit} - -# Tool runner (auto-execution) -for message in client.beta.messages.tool_runner( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather], - messages=[{"role": "user", "content": "What's the weather in NYC?"}] -): - if message.stop_reason == "end_turn": - print(message.content[0].text) - -# Manual tool handling -message = client.messages.create( - model="claude-sonnet-4-5-20250929", - max_tokens=1024, - tools=[get_weather.to_param()], - messages=[...] -) - -# Extract tool use -for block in message.content: - if block.type == "tool_use": - result = get_weather(**block.input) - # Send result back in next request -``` - -## See Also - -- **[Complete Tools Documentation](../api/tools.md)** - Full details and examples -- **[Tool Integration Tasks](../common-tasks/tool-integration.md)** - Task-oriented guide -- **[Tool Usage Guide](../guides/tool-usage.md)** - Advanced patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md deleted file mode 100644 index 52b295c69..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/client-config.md +++ /dev/null @@ -1,305 +0,0 @@ -# Client Configuration Reference - -Initialize and configure Anthropic API clients for synchronous and asynchronous operations. - -## Synchronous Client - -```python { .api } -class Anthropic: - """Synchronous client for Anthropic API.""" - - def __init__( - self, - *, - api_key: str | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: dict[str, str] | None = None, - default_query: dict[str, object] | None = None, - http_client: httpx.Client | None = None, - ): - """ - Initialize Anthropic client. - - Parameters: - api_key: API key (defaults to ANTHROPIC_API_KEY env var) - auth_token: Bearer token (alternative to api_key) - base_url: Override base URL (defaults to ANTHROPIC_BASE_URL env var) - timeout: Request timeout (default: 600s) - max_retries: Maximum retry attempts (default: 2) - default_headers: Headers added to all requests - default_query: Query parameters added to all requests - http_client: Custom httpx.Client instance - """ - ... - - def close(self) -> None: - """Close the underlying HTTP client.""" - ... - - def __enter__(self) -> Anthropic: - """Context manager entry.""" - ... - - def __exit__(self, exc_type, exc_val, exc_tb) -> None: - """Context manager exit.""" - ... - - @property - def messages(self) -> Messages: - """Access Messages resource.""" - ... - - @property - def beta(self) -> Beta: - """Access Beta resources.""" - ... - - @property - def models(self) -> Models: - """Access Models resource.""" - ... -``` - -## Asynchronous Client - -```python { .api } -class AsyncAnthropic: - """Asynchronous client for Anthropic API.""" - - def __init__( - self, - *, - api_key: str | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout = DEFAULT_TIMEOUT, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: dict[str, str] | None = None, - default_query: dict[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - ): - """Initialize async Anthropic client. Same parameters as Anthropic.""" - ... - - async def close(self) -> None: - """Close the underlying HTTP client.""" - ... - - async def __aenter__(self) -> AsyncAnthropic: - """Async context manager entry.""" - ... - - async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: - """Async context manager exit.""" - ... -``` - -## Constants - -```python { .api } -DEFAULT_TIMEOUT: float = 600.0 # 10 minutes -DEFAULT_MAX_RETRIES: int = 2 -``` - -## Quick Examples - -### Basic Initialization - -```python -from anthropic import Anthropic - -# Using environment variable ANTHROPIC_API_KEY -client = Anthropic() - -# Explicit API key -client = Anthropic(api_key="your-api-key") -``` - -### Context Manager - -```python -with Anthropic() as client: - message = client.messages.create(...) -# Client automatically closed -``` - -### Custom Timeout - -```python -import httpx - -# Single timeout value (applies to all) -client = Anthropic(timeout=120.0) - -# Granular timeout control -client = Anthropic( - timeout=httpx.Timeout( - connect=10.0, # Connection timeout - read=60.0, # Read timeout - write=10.0, # Write timeout - pool=10.0, # Pool timeout - ) -) -``` - -### Retry Configuration - -```python -# Increase retries -client = Anthropic(max_retries=5) - -# Disable retries -client = Anthropic(max_retries=0) -``` - -### Custom Headers - -```python -client = Anthropic( - default_headers={ - "X-Custom-Header": "value", - "User-Agent": "MyApp/1.0", - } -) -``` - -### Custom Base URL - -```python -# Development environment -client = Anthropic( - base_url="https://api.dev.anthropic.com" -) - -# Using environment variable ANTHROPIC_BASE_URL -import os -os.environ["ANTHROPIC_BASE_URL"] = "https://api.dev.anthropic.com" -client = Anthropic() -``` - -### Custom HTTP Client - -```python -import httpx - -# Custom httpx client -http_client = httpx.Client( - proxy="http://proxy.example.com:8080", - limits=httpx.Limits(max_connections=100), -) - -client = Anthropic(http_client=http_client) -``` - -### Async Client - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - message = await client.messages.create(...) - await client.close() - -asyncio.run(main()) -``` - -### Async Context Manager - -```python -async def main(): - async with AsyncAnthropic() as client: - message = await client.messages.create(...) - # Client automatically closed -``` - -### Bearer Token Authentication - -```python -client = Anthropic(auth_token="your-bearer-token") -``` - -### HTTP Client with Proxy - -```python -import httpx - -client = Anthropic( - http_client=httpx.Client( - proxy="http://proxy.example.com:8080" - ) -) -``` - -### Connection Pool Configuration - -```python -import httpx - -client = Anthropic( - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=100, - max_keepalive_connections=20, - ) - ) -) -``` - -### Request Per-Call Options - -```python -# Override timeout for specific request -message = client.messages.with_options(timeout=120.0).create(...) - -# Override headers -message = client.messages.with_options( - headers={"X-Request-ID": "abc123"} -).create(...) - -# Override max retries -message = client.messages.with_options(max_retries=5).create(...) -``` - -## HTTP Client Factories - -```python { .api } -class DefaultHttpxClient: - """Default synchronous HTTP client using httpx.""" - def __init__( - self, - *, - proxy: str | httpx.Proxy | None = None, - transport: httpx.HTTPTransport | None = None, - **kwargs - ): ... - -class DefaultAsyncHttpxClient: - """Default asynchronous HTTP client using httpx.""" - def __init__( - self, - *, - proxy: str | httpx.Proxy | None = None, - transport: httpx.AsyncHTTPTransport | None = None, - **kwargs - ): ... - -class DefaultAioHttpClient: - """Alternative async client using aiohttp (better concurrency).""" - def __init__(self, **kwargs): ... -``` - -## Environment Variables - -- `ANTHROPIC_API_KEY` - API key for authentication -- `ANTHROPIC_BASE_URL` - Override base URL -- `ANTHROPIC_AUTH_TOKEN` - Bearer token (alternative to API key) - -## See Also - -- [Error Handling](./errors.md) - Exception handling and retry logic -- [Utilities](./utilities.md) - HTTP client utilities diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md deleted file mode 100644 index 60f490e56..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/errors.md +++ /dev/null @@ -1,287 +0,0 @@ -# Error Handling Reference - -Complete exception hierarchy and error handling patterns for the Anthropic Python SDK. - -## Exception Hierarchy - -```python { .api } -AnthropicError -├── APIError -│ ├── APIStatusError -│ │ ├── BadRequestError (400) -│ │ ├── AuthenticationError (401) -│ │ ├── PermissionDeniedError (403) -│ │ ├── NotFoundError (404) -│ │ ├── ConflictError (409) -│ │ ├── RequestTooLargeError (413) -│ │ ├── UnprocessableEntityError (422) -│ │ ├── RateLimitError (429) -│ │ ├── InternalServerError (≥500) -│ │ ├── ServiceUnavailableError (503) -│ │ ├── DeadlineExceededError (504) -│ │ └── OverloadedError (529) -│ ├── APIConnectionError -│ ├── APITimeoutError -│ └── APIResponseValidationError -``` - -## Exception Classes - -### Base Exceptions - -```python { .api } -class AnthropicError(Exception): - """Base exception for all Anthropic errors.""" - ... - -class APIError(AnthropicError): - """Base for all API-related errors.""" - message: str - request: httpx.Request | None - body: object | None -``` - -### HTTP Status Errors - -```python { .api } -class APIStatusError(APIError): - """HTTP status code error.""" - response: httpx.Response - status_code: int - request_id: str | None - -class BadRequestError(APIStatusError): - """400 - Invalid request.""" - ... - -class AuthenticationError(APIStatusError): - """401 - Invalid API key.""" - ... - -class PermissionDeniedError(APIStatusError): - """403 - Insufficient permissions.""" - ... - -class NotFoundError(APIStatusError): - """404 - Resource not found.""" - ... - -class ConflictError(APIStatusError): - """409 - Request conflicts with current state.""" - ... - -class RequestTooLargeError(APIStatusError): - """413 - Request payload too large.""" - ... - -class UnprocessableEntityError(APIStatusError): - """422 - Request semantically invalid.""" - ... - -class RateLimitError(APIStatusError): - """429 - Rate limit exceeded.""" - ... - -class InternalServerError(APIStatusError): - """500+ - Server error.""" - ... - -class ServiceUnavailableError(APIStatusError): - """503 - Service temporarily unavailable.""" - ... - -class DeadlineExceededError(APIStatusError): - """504 - Request exceeded deadline.""" - ... - -class OverloadedError(APIStatusError): - """529 - Service overloaded.""" - ... -``` - -### Connection Errors - -```python { .api } -class APIConnectionError(APIError): - """Failed to connect to API.""" - ... - -class APITimeoutError(APIError): - """Request timed out.""" - ... - -class APIResponseValidationError(APIError): - """Response validation failed.""" - ... -``` - -## Error Response Format - -```python { .api } -class ErrorObject(BaseModel): - """Error object in API responses.""" - type: str - message: str - -class ErrorResponse(BaseModel): - """Error response wrapper.""" - type: Literal["error"] - error: ErrorObject -``` - -## Quick Examples - -### Basic Error Handling - -```python -from anthropic import APIError - -try: - message = client.messages.create(...) -except APIError as e: - print(f"Error: {e.message}") -``` - -### Handle Specific Errors - -```python -from anthropic import ( - RateLimitError, - AuthenticationError, - BadRequestError, -) - -try: - message = client.messages.create(...) -except RateLimitError as e: - retry_after = e.response.headers.get("retry-after") - print(f"Rate limited. Retry after {retry_after}s") -except AuthenticationError: - print("Invalid API key") -except BadRequestError as e: - print(f"Invalid request: {e.message}") -``` - -### Retry with Exponential Backoff - -```python -import time -from anthropic import RateLimitError, InternalServerError - -def create_message_with_retry(max_retries=3): - for attempt in range(max_retries): - try: - return client.messages.create(...) - except (RateLimitError, InternalServerError) as e: - if attempt == max_retries - 1: - raise - wait_time = 2 ** attempt - print(f"Retry {attempt + 1} after {wait_time}s") - time.sleep(wait_time) -``` - -### Extract Request ID - -```python -from anthropic import APIStatusError - -try: - message = client.messages.create(...) -except APIStatusError as e: - print(f"Request ID: {e.request_id}") - print(f"Status: {e.status_code}") -``` - -### Validate Response - -```python -from anthropic import APIResponseValidationError - -try: - message = client.messages.create(...) -except APIResponseValidationError as e: - print(f"Response validation failed: {e.message}") -``` - -### Handle All Errors - -```python -from anthropic import ( - APIError, - APIConnectionError, - APITimeoutError, - RateLimitError, -) - -try: - message = client.messages.create(...) -except APITimeoutError: - print("Request timed out") -except APIConnectionError: - print("Connection failed") -except RateLimitError: - print("Rate limit exceeded") -except APIError as e: - print(f"API error: {e.message}") -``` - -## Best Practices - -### Always Handle Exceptions - -```python -# Bad -message = client.messages.create(...) - -# Good -try: - message = client.messages.create(...) -except APIError as e: - # Handle error - ... -``` - -### Use Specific Exception Types - -```python -# Less precise -try: - message = client.messages.create(...) -except Exception: - ... - -# More precise -try: - message = client.messages.create(...) -except RateLimitError: - # Handle rate limit - ... -except APIError: - # Handle other API errors - ... -``` - -### Log Request Context - -```python -import logging - -logger = logging.getLogger(__name__) - -try: - message = client.messages.create(...) -except APIStatusError as e: - logger.error( - "API request failed", - extra={ - "request_id": e.request_id, - "status_code": e.status_code, - "error_message": e.message, - } - ) -``` - -## See Also - -- [Client Configuration](./client-config.md) - Configure retry and timeout behavior -- [Error Handling Guide](../guides/error-handling.md) - Advanced error handling patterns diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md deleted file mode 100644 index bfd82a606..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/pagination.md +++ /dev/null @@ -1,615 +0,0 @@ -# Pagination Reference - -Auto-paginating iterators for list operations with manual control options. The SDK provides three pagination types: ID-based, token-based, and cursor-based. - -## Pagination Classes - -### Synchronous Pagination - -```python { .api } -class SyncPage(Generic[T]): - """ - Synchronous ID-based pagination. - - Provides: - - Automatic iteration over all items - - Manual page control - - Page metadata access - - Attributes: - data: Items in current page - """ - data: list[T] - - def __iter__(self) -> Iterator[T]: - """ - Iterate over all items across all pages automatically. - - Yields: - Individual items from current and subsequent pages - """ - ... - - def __getitem__(self, index: int) -> T: - """ - Get item by index in current page. - - Parameters: - index: Item index in current page - - Returns: - Item at index - - Raises: - IndexError: If index out of range - """ - ... - - def has_next_page(self) -> bool: - """ - Check if another page exists. - - Returns: - True if next page available - """ - ... - - def next_page_info(self) -> dict[str, Any]: - """ - Get information needed to fetch next page. - - Returns: - Dictionary with pagination parameters (after_id, limit, etc.) - """ - ... - - def get_next_page(self) -> SyncPage[T]: - """ - Fetch next page. - - Returns: - New SyncPage for next page - - Raises: - ValueError: If no next page exists - """ - ... - -class SyncTokenPage(Generic[T]): - """ - Synchronous token-based pagination. - - Similar to SyncPage but uses continuation tokens instead of IDs. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __iter__(self) -> Iterator[T]: - """Iterate over all items automatically.""" - ... - - def has_next_page(self) -> bool: - """Check if next page exists.""" - ... - - def next_page_info(self) -> dict[str, Any]: - """Get next page token.""" - ... - - def get_next_page(self) -> SyncTokenPage[T]: - """Fetch next page.""" - ... - -class SyncPageCursor(Generic[T]): - """ - Synchronous cursor-based pagination. - - Uses cursors for pagination instead of IDs or tokens. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __iter__(self) -> Iterator[T]: - """Iterate over all items automatically.""" - ... - - def has_next_page(self) -> bool: - """Check if next page exists.""" - ... - - def next_page_info(self) -> dict[str, Any]: - """Get next page cursor.""" - ... - - def get_next_page(self) -> SyncPageCursor[T]: - """Fetch next page.""" - ... -``` - -### Asynchronous Pagination - -```python { .api } -class AsyncPage(Generic[T]): - """ - Asynchronous ID-based pagination. - - Provides: - - Async automatic iteration over all items - - Async manual page control - - Page metadata access - - Attributes: - data: Items in current page - """ - data: list[T] - - def __aiter__(self) -> AsyncIterator[T]: - """ - Async iterate over all items across all pages. - - Yields: - Individual items from current and subsequent pages - """ - ... - - def __getitem__(self, index: int) -> T: - """Get item by index in current page.""" - ... - - async def has_next_page(self) -> bool: - """Check if another page exists.""" - ... - - async def next_page_info(self) -> dict[str, Any]: - """Get information needed to fetch next page.""" - ... - - async def get_next_page(self) -> AsyncPage[T]: - """Fetch next page.""" - ... - -class AsyncTokenPage(Generic[T]): - """ - Asynchronous token-based pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __aiter__(self) -> AsyncIterator[T]: ... - async def has_next_page(self) -> bool: ... - async def next_page_info(self) -> dict[str, Any]: ... - async def get_next_page(self) -> AsyncTokenPage[T]: ... - -class AsyncPageCursor(Generic[T]): - """ - Asynchronous cursor-based pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __aiter__(self) -> AsyncIterator[T]: ... - async def has_next_page(self) -> bool: ... - async def next_page_info(self) -> dict[str, Any]: ... - async def get_next_page(self) -> AsyncPageCursor[T]: ... -``` - -## Usage Examples - -### Auto-Pagination (Simple) - -```python -from anthropic import Anthropic - -client = Anthropic() - -# Automatically iterate over all batches -for batch in client.messages.batches.list(): - print(f"Batch {batch.id}: {batch.processing_status}") -``` - -### Auto-Pagination with Limit - -```python -# Get first 100 batches total (auto-fetching pages) -count = 0 -for batch in client.messages.batches.list(limit=20): - print(batch.id) - count += 1 - if count >= 100: - break -``` - -### Manual Pagination - -```python -# Get first page -page = client.messages.batches.list(limit=10) - -# Process first page -for batch in page.data: - print(f"Batch: {batch.id}") - -# Check if more pages -if page.has_next_page(): - print("More pages available") - - # Get next page info - next_info = page.next_page_info() - print(f"Next page params: {next_info}") - - # Fetch next page - next_page = page.get_next_page() - for batch in next_page.data: - print(f"Next page batch: {batch.id}") -``` - -### Iterate All Pages Manually - -```python -page = client.messages.batches.list(limit=10) - -while True: - # Process current page - for batch in page.data: - print(batch.id) - - # Check for next page - if not page.has_next_page(): - break - - # Fetch next page - page = page.get_next_page() -``` - -### Pagination with Before/After - -```python -# Get batches after specific ID -page = client.messages.batches.list( - after_id="batch_abc123", - limit=20 -) - -for batch in page: - print(batch.id) - -# Get batches before specific ID -page = client.messages.batches.list( - before_id="batch_xyz789", - limit=20 -) - -for batch in page: - print(batch.id) -``` - -### Access Current Page Data - -```python -page = client.messages.batches.list(limit=5) - -# Get items in current page -items = page.data -print(f"Current page has {len(items)} items") - -# Access by index -first_item = page[0] -print(f"First item: {first_item.id}") -``` - -### Async Auto-Pagination - -```python -import asyncio -from anthropic import AsyncAnthropic - -async def main(): - client = AsyncAnthropic() - - # Automatically iterate over all batches - async for batch in client.messages.batches.list(): - print(f"Batch {batch.id}: {batch.processing_status}") - -asyncio.run(main()) -``` - -### Async Manual Pagination - -```python -async def paginate_manually(): - client = AsyncAnthropic() - - # Get first page - page = await client.messages.batches.list(limit=10) - - # Process first page - for batch in page.data: - print(batch.id) - - # Check and fetch next page - if await page.has_next_page(): - next_page = await page.get_next_page() - for batch in next_page.data: - print(batch.id) - -asyncio.run(paginate_manually()) -``` - -### Async Iterate All Pages - -```python -async def iterate_all_pages(): - client = AsyncAnthropic() - - page = await client.messages.batches.list(limit=10) - - while True: - for batch in page.data: - print(batch.id) - - if not await page.has_next_page(): - break - - page = await page.get_next_page() - -asyncio.run(iterate_all_pages()) -``` - -### Pagination with Processing - -```python -# Collect all batches with filtering -completed_batches = [] - -for batch in client.messages.batches.list(): - if batch.processing_status == "ended": - completed_batches.append(batch) - -print(f"Found {len(completed_batches)} completed batches") -``` - -### Limit Total Items - -```python -# Get exactly 50 items total -items = [] -for batch in client.messages.batches.list(limit=20): - items.append(batch) - if len(items) >= 50: - break - -print(f"Collected {len(items)} items") -``` - -### Paginate with Error Handling - -```python -from anthropic import APIError - -try: - for batch in client.messages.batches.list(): - print(batch.id) -except APIError as e: - print(f"Pagination error: {e}") -``` - -### Concurrent Page Fetching - -```python -import asyncio - -async def fetch_multiple_pages(): - client = AsyncAnthropic() - - # Fetch first page - page1 = await client.messages.batches.list(limit=10) - - # Fetch multiple subsequent pages concurrently - if await page1.has_next_page(): - next_info = await page1.next_page_info() - - pages = await asyncio.gather( - page1.get_next_page(), - client.messages.batches.list(**next_info, limit=10), - ) - - for page in pages: - for batch in page.data: - print(batch.id) - -asyncio.run(fetch_multiple_pages()) -``` - -### List Models with Pagination - -```python -# Auto-paginate through all models -for model in client.models.list(): - print(f"{model.id}: {model.display_name}") -``` - -### Count Items with Pagination - -```python -# Count total items -total = 0 -for batch in client.messages.batches.list(): - total += 1 - -print(f"Total batches: {total}") -``` - -### Pagination Performance - -```python -import time - -start = time.time() - -# Efficient pagination with larger page size -count = 0 -for batch in client.messages.batches.list(limit=100): - count += 1 - -elapsed = time.time() - start -print(f"Processed {count} items in {elapsed:.2f} seconds") -``` - -### Page Metadata - -```python -page = client.messages.batches.list(limit=10) - -print(f"Items in page: {len(page.data)}") -print(f"Has next: {page.has_next_page()}") - -if page.has_next_page(): - next_info = page.next_page_info() - print(f"Next page info: {next_info}") -``` - -### Reverse Pagination - -```python -# Get most recent items first (default) -for batch in client.messages.batches.list(limit=10): - print(f"Recent: {batch.id} - {batch.created_at}") - -# Get older items using before_id -oldest_on_page = None -for batch in page.data: - oldest_on_page = batch.id - -if oldest_on_page: - older_page = client.messages.batches.list(before_id=oldest_on_page, limit=10) - for batch in older_page: - print(f"Older: {batch.id}") -``` - -### Collect All Items - -```python -# Collect all items into list -all_batches = [] -for batch in client.messages.batches.list(): - all_batches.append(batch) - -print(f"Total batches: {len(all_batches)}") -``` - -### Batch Processing Pages - -```python -def process_batch(batches): - """Process a batch of items.""" - for batch in batches: - print(f"Processing {batch.id}") - -page = client.messages.batches.list(limit=50) -while True: - process_batch(page.data) - - if not page.has_next_page(): - break - - page = page.get_next_page() -``` - -### Custom Page Size - -```python -# Use small pages for frequent updates -for batch in client.messages.batches.list(limit=5): - print(batch.id) - -# Use large pages for bulk processing -for batch in client.messages.batches.list(limit=100): - print(batch.id) -``` - -## JSONL Stream Decoders - -For batch results that return JSONL streams: - -```python { .api } -from typing import Generic, TypeVar, Iterator, AsyncIterator - -T = TypeVar('T') - -class JSONLDecoder(Generic[T]): - """ - Synchronous JSONL stream decoder. - - Decodes newline-delimited JSON objects from streaming responses, - commonly used for batch result streaming. - - Yields: - Decoded objects of type T, one per JSONL line - """ - - def __iter__(self) -> Iterator[T]: - """Iterate over decoded JSONL objects.""" - ... - -class AsyncJSONLDecoder(Generic[T]): - """ - Asynchronous JSONL stream decoder. - - Async version for decoding JSONL streams. - - Yields: - Decoded objects of type T, one per JSONL line - """ - - def __aiter__(self) -> AsyncIterator[T]: - """Async iterate over decoded JSONL objects.""" - ... -``` - -### JSONL Decoder Usage - -```python -# Stream batch results (returns JSONL decoder) -results = client.messages.batches.results("batch_abc123") - -# Iterate over individual results -for result in results: - if result.result.type == "succeeded": - print(f"Message: {result.result.message.content[0].text}") - elif result.result.type == "errored": - print(f"Error: {result.result.error.message}") -``` - -### Async JSONL Decoding - -```python -import asyncio - -async def process_batch_results(): - client = AsyncAnthropic() - - # Get async JSONL decoder - results = await client.messages.batches.results("batch_abc123") - - # Async iterate - async for result in results: - print(f"Custom ID: {result.custom_id}") - # Process result - -asyncio.run(process_batch_results()) -``` - -## See Also - -- [Messages API](../api/messages.md) - Message pagination -- [Batches API](../api/batches.md) - Batch pagination and JSONL results -- [Models API](../api/models.md) - Model pagination -- [Type System](./types.md) - Pagination type definitions diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md deleted file mode 100644 index f1848d563..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/types.md +++ /dev/null @@ -1,1413 +0,0 @@ -# Type System Reference - -Comprehensive type definitions for all request parameters and response objects using Pydantic models and TypedDict. All types are fully typed for static analysis and IDE support. - -## Core Message Types - -### Message - -```python { .api } -class Message(BaseModel): - """ - Complete message response from Claude. - - Attributes: - id: Unique message identifier (starts with "msg_") - type: Always "message" - role: Always "assistant" - content: List of content blocks (text, tool_use, etc.) - model: Model identifier used for generation - stop_reason: Why generation stopped - stop_sequence: Stop sequence that triggered completion (if any) - usage: Token usage statistics - """ - id: str - type: Literal["message"] - role: Literal["assistant"] - content: list[ContentBlock] - model: str - stop_reason: StopReason | None - stop_sequence: str | None - usage: Usage -``` - -### MessageParam - -```python { .api } -class MessageParam(TypedDict): - """ - User or assistant message in conversation. - - Fields: - role: "user" or "assistant" - content: String or list of content blocks - """ - role: Literal["user", "assistant"] - content: str | list[ContentBlockParam] -``` - -### ContentBlock (Response) - -```python { .api } -ContentBlock = Union[TextBlock, ToolUseBlock] - -class TextBlock(BaseModel): - """ - Text content in assistant response. - - Attributes: - type: Always "text" - text: The text content - """ - type: Literal["text"] - text: str - -class ToolUseBlock(BaseModel): - """ - Tool invocation in assistant response. - - Attributes: - type: Always "tool_use" - id: Unique tool call identifier - name: Tool name - input: Tool input parameters as dict - """ - type: Literal["tool_use"] - id: str - name: str - input: dict[str, Any] -``` - -### ContentBlockParam (Request) - -```python { .api } -ContentBlockParam = Union[ - TextBlockParam, - ImageBlockParam, - DocumentBlockParam, - ToolUseBlockParam, - ToolResultBlockParam, -] - -class TextBlockParam(TypedDict): - """ - Text content in message. - - Fields: - type: Always "text" - text: The text content - cache_control: Optional cache control - """ - type: Literal["text"] - text: str - cache_control: NotRequired[CacheControlEphemeral] - -class ImageBlockParam(TypedDict): - """ - Image content in message. - - Fields: - type: Always "image" - source: Image source (base64 or URL) - cache_control: Optional cache control - """ - type: Literal["image"] - source: Base64ImageSource | URLImageSource - cache_control: NotRequired[CacheControlEphemeral] - -class DocumentBlockParam(TypedDict): - """ - Document content (PDF, text) in message. - - Fields: - type: Always "document" - source: Document source (base64 or URL) - cache_control: Optional cache control - """ - type: Literal["document"] - source: Base64PDFSource | URLPDFSource | PlainTextSource - cache_control: NotRequired[CacheControlEphemeral] - -class ToolUseBlockParam(TypedDict): - """ - Tool invocation in assistant message (for conversation history). - - Fields: - type: Always "tool_use" - id: Tool call identifier - name: Tool name - input: Tool input parameters - cache_control: Optional cache control - """ - type: Literal["tool_use"] - id: str - name: str - input: dict[str, Any] - cache_control: NotRequired[CacheControlEphemeral] - -class ToolResultBlockParam(TypedDict): - """ - Tool result in user message. - - Fields: - type: Always "tool_result" - tool_use_id: ID of tool invocation this is result for - content: String or list of content blocks - is_error: Whether result represents an error - cache_control: Optional cache control - """ - type: Literal["tool_result"] - tool_use_id: str - content: NotRequired[str | list[TextBlockParam | ImageBlockParam]] - is_error: NotRequired[bool] - cache_control: NotRequired[CacheControlEphemeral] -``` - -## Content Source Types - -### Image Sources - -```python { .api } -class Base64ImageSource(TypedDict): - """ - Base64-encoded image. - - Fields: - type: Always "base64" - media_type: MIME type (jpeg, png, gif, webp) - data: Base64-encoded image data (without data URL prefix) - """ - type: Literal["base64"] - media_type: Literal["image/jpeg", "image/png", "image/gif", "image/webp"] - data: str - -class URLImageSource(TypedDict): - """ - Image from URL. - - Fields: - type: Always "url" - url: Image URL (must be publicly accessible) - """ - type: Literal["url"] - url: str -``` - -### Document Sources - -```python { .api } -class Base64PDFSource(TypedDict): - """ - Base64-encoded PDF document. - - Fields: - type: Always "base64" - media_type: Always "application/pdf" - data: Base64-encoded PDF data - """ - type: Literal["base64"] - media_type: Literal["application/pdf"] - data: str - -class URLPDFSource(TypedDict): - """ - PDF document from URL. - - Fields: - type: Always "url" - media_type: Always "application/pdf" - url: PDF URL - """ - type: Literal["url"] - media_type: Literal["application/pdf"] - url: str - -class PlainTextSource(TypedDict): - """ - Plain text document. - - Fields: - type: Always "text" - media_type: Always "text/plain" - data: Plain text content - """ - type: Literal["text"] - media_type: Literal["text/plain"] - data: str -``` - -## Tool Types - -### Tool Definition - -```python { .api } -class Tool(BaseModel): - """ - Tool definition in response. - - Attributes: - name: Tool name - description: Tool description - input_schema: JSON Schema for parameters - """ - name: str - description: str - input_schema: dict[str, Any] - -class ToolParam(TypedDict): - """ - Tool definition in request. - - Fields: - name: Tool name (alphanumeric + underscores) - description: What the tool does - input_schema: JSON Schema for tool parameters - cache_control: Optional cache control - """ - name: str - description: str - input_schema: dict[str, Any] - cache_control: NotRequired[CacheControlEphemeral] -``` - -### Tool Choice - -```python { .api } -ToolChoice = Union[ - ToolChoiceAuto, - ToolChoiceAny, - ToolChoiceNone, - ToolChoiceTool, -] - -class ToolChoiceAuto(TypedDict): - """ - Let Claude decide whether to use tools (default). - - Fields: - type: Always "auto" - disable_parallel_tool_use: Disable parallel tool calls - """ - type: Literal["auto"] - disable_parallel_tool_use: NotRequired[bool] - -class ToolChoiceAny(TypedDict): - """ - Force Claude to use at least one tool. - - Fields: - type: Always "any" - disable_parallel_tool_use: Disable parallel tool calls - """ - type: Literal["any"] - disable_parallel_tool_use: NotRequired[bool] - -class ToolChoiceNone(TypedDict): - """ - Disable all tool use. - - Fields: - type: Always "none" - """ - type: Literal["none"] - -class ToolChoiceTool(TypedDict): - """ - Force Claude to use specific tool. - - Fields: - type: Always "tool" - name: Tool name to use - disable_parallel_tool_use: Disable parallel tool calls - """ - type: Literal["tool"] - name: str - disable_parallel_tool_use: NotRequired[bool] -``` - -## Configuration Types - -### Usage - -```python { .api } -class Usage(BaseModel): - """ - Token usage statistics. - - Attributes: - input_tokens: Number of input tokens - output_tokens: Number of output tokens - cache_creation_input_tokens: Tokens used to create cache (if using prompt caching) - cache_read_input_tokens: Tokens read from cache (if using prompt caching) - """ - input_tokens: int - output_tokens: int - cache_creation_input_tokens: int | None = None - cache_read_input_tokens: int | None = None -``` - -### Metadata - -```python { .api } -class Metadata(BaseModel): - """ - Response metadata. - - Attributes: - user_id: End-user identifier (if provided) - """ - user_id: str | None = None - -class MetadataParam(TypedDict, total=False): - """ - Request metadata for tracking and compliance. - - Fields: - user_id: End-user identifier for tracking and abuse prevention - """ - user_id: str -``` - -### Cache Control - -```python { .api } -class CacheControlEphemeral(TypedDict): - """ - Ephemeral cache control for prompt caching. - - Fields: - type: Always "ephemeral" - """ - type: Literal["ephemeral"] -``` - -### Thinking Configuration - -```python { .api } -ThinkingConfigParam = Union[ThinkingConfigEnabled, ThinkingConfigDisabled] - -class ThinkingConfigEnabled(TypedDict): - """ - Enable extended thinking for enhanced reasoning. - - Fields: - type: Always "enabled" - """ - type: Literal["enabled"] - -class ThinkingConfigDisabled(TypedDict): - """ - Disable extended thinking. - - Fields: - type: Always "disabled" - """ - type: Literal["disabled"] -``` - -### Stop Reason - -```python { .api } -StopReason = Literal[ - "end_turn", # Natural completion - "max_tokens", # Hit max_tokens limit - "stop_sequence", # Hit custom stop sequence - "tool_use", # Model wants to use a tool -] -``` - -## Token Counting Types - -```python { .api } -class MessageTokensCount(BaseModel): - """ - Token count response. - - Attributes: - input_tokens: Number of input tokens - """ - input_tokens: int -``` - -## Batch Types - -### Batch - -```python { .api } -class MessageBatch(BaseModel): - """ - Batch metadata and status. - - Attributes: - id: Unique batch identifier (starts with "msgbatch_") - type: Always "message_batch" - processing_status: Current processing status - request_counts: Counts by result type - ended_at: When batch completed (ISO 8601) - created_at: When batch was created (ISO 8601) - expires_at: When batch results will expire (ISO 8601) - cancel_initiated_at: When cancellation was initiated (ISO 8601) - results_url: URL to download results JSONL - """ - id: str - type: Literal["message_batch"] - processing_status: Literal["in_progress", "canceling", "ended"] - request_counts: MessageBatchRequestCounts - ended_at: str | None - created_at: str - expires_at: str - cancel_initiated_at: str | None - results_url: str | None - -class MessageBatchRequestCounts(BaseModel): - """ - Request count statistics in batch. - - Attributes: - processing: Requests currently processing - succeeded: Successful requests - errored: Failed requests - canceled: Canceled requests - expired: Expired requests - """ - processing: int - succeeded: int - errored: int - canceled: int - expired: int - -class DeletedMessageBatch(BaseModel): - """ - Deleted batch confirmation. - - Attributes: - id: Batch identifier that was deleted - type: Always "message_batch_deleted" - """ - id: str - type: Literal["message_batch_deleted"] -``` - -### Batch Request/Response - -```python { .api } -class MessageBatchIndividualRequest(TypedDict): - """ - Individual request in batch. - - Fields: - custom_id: Client-provided unique identifier - params: Message creation parameters - """ - custom_id: str - params: MessageCreateParams - -class MessageBatchIndividualResponse(BaseModel): - """ - Individual response in batch results. - - Attributes: - custom_id: Client-provided identifier from request - result: Result object (success/error/canceled/expired) - """ - custom_id: str - result: MessageBatchResult - -MessageBatchResult = Union[ - MessageBatchSucceededResult, - MessageBatchErroredResult, - MessageBatchCanceledResult, - MessageBatchExpiredResult, -] - -class MessageBatchSucceededResult(BaseModel): - """ - Successful batch result. - - Attributes: - type: Always "succeeded" - message: The message response - """ - type: Literal["succeeded"] - message: Message - -class MessageBatchErroredResult(BaseModel): - """ - Failed batch result. - - Attributes: - type: Always "errored" - error: Error details - """ - type: Literal["errored"] - error: ErrorObject - -class MessageBatchCanceledResult(BaseModel): - """ - Canceled batch result. - - Attributes: - type: Always "canceled" - """ - type: Literal["canceled"] - -class MessageBatchExpiredResult(BaseModel): - """ - Expired batch result. - - Attributes: - type: Always "expired" - """ - type: Literal["expired"] -``` - -## Model Types - -```python { .api } -class ModelInfo(BaseModel): - """ - Model information and capabilities. - - Attributes: - id: Model identifier - type: Always "model" - display_name: Human-readable model name - created_at: When model was created (ISO 8601) - """ - id: str - type: Literal["model"] - display_name: str - created_at: str - -Model = Literal[ - "claude-opus-4-5-20250929", - "claude-sonnet-4-5-20250929", - "claude-3-5-sonnet-20241022", - "claude-3-5-sonnet-20240620", - "claude-3-5-haiku-20241022", - "claude-3-opus-20240229", - "claude-3-sonnet-20240229", - "claude-3-haiku-20240307", - # Additional models... -] -``` - -## Error Types - -```python { .api } -class ErrorObject(BaseModel): - """ - Error object in API responses. - - Attributes: - type: Error type identifier - message: Human-readable error message - """ - type: str - message: str - -class ErrorResponse(BaseModel): - """ - Error response wrapper. - - Attributes: - type: Always "error" - error: Error details - """ - type: Literal["error"] - error: APIErrorObject - -class APIErrorObject(BaseModel): - """ - API error object. - - Attributes: - type: Error type - message: Error message - """ - type: str - message: str -``` - -## Stream Event Types - -### Message Stream Events - -```python { .api } -MessageStreamEvent = Union[ - MessageStartEvent, - MessageDeltaEvent, - MessageStopEvent, - ContentBlockStartEvent, - ContentBlockDeltaEvent, - ContentBlockStopEvent, -] - -class MessageStartEvent(BaseModel): - """ - Stream started with initial message. - - Attributes: - type: Always "message_start" - message: Initial message with empty content - """ - type: Literal["message_start"] - message: Message - -class MessageDeltaEvent(BaseModel): - """ - Message metadata changed. - - Attributes: - type: Always "message_delta" - delta: Changed fields - usage: Token usage update - """ - type: Literal["message_delta"] - delta: MessageDelta - usage: MessageDeltaUsage - -class MessageDelta(BaseModel): - """ - Message field changes. - - Attributes: - stop_reason: Updated stop reason - stop_sequence: Stop sequence that triggered - """ - stop_reason: StopReason | None - stop_sequence: str | None - -class MessageDeltaUsage(BaseModel): - """ - Token usage in stream delta. - - Attributes: - output_tokens: Output tokens generated so far - """ - output_tokens: int - -class MessageStopEvent(BaseModel): - """ - Stream completed. - - Attributes: - type: Always "message_stop" - """ - type: Literal["message_stop"] -``` - -### Content Block Stream Events - -```python { .api } -class ContentBlockStartEvent(BaseModel): - """ - New content block started. - - Attributes: - type: Always "content_block_start" - index: Content block index - content_block: Initial content block - """ - type: Literal["content_block_start"] - index: int - content_block: ContentBlock - -class ContentBlockDeltaEvent(BaseModel): - """ - Content block received delta. - - Attributes: - type: Always "content_block_delta" - index: Content block index - delta: Delta content - """ - type: Literal["content_block_delta"] - index: int - delta: ContentBlockDelta - -ContentBlockDelta = Union[TextDelta, InputJSONDelta] - -class TextDelta(BaseModel): - """ - Text content delta. - - Attributes: - type: Always "text_delta" - text: Incremental text - """ - type: Literal["text_delta"] - text: str - -class InputJSONDelta(BaseModel): - """ - Tool input JSON delta. - - Attributes: - type: Always "input_json_delta" - partial_json: Incremental JSON string - """ - type: Literal["input_json_delta"] - partial_json: str - -class ContentBlockStopEvent(BaseModel): - """ - Content block completed. - - Attributes: - type: Always "content_block_stop" - index: Content block index - """ - type: Literal["content_block_stop"] - index: int -``` - -### Raw Stream Events - -```python { .api } -RawMessageStreamEvent = Union[ - RawMessageStartEvent, - RawMessageDeltaEvent, - RawMessageStopEvent, - RawContentBlockStartEvent, - RawContentBlockDeltaEvent, - RawContentBlockStopEvent, -] - -class RawMessageStartEvent(BaseModel): - """ - Raw message start event from SSE. - - Attributes: - type: Always "message_start" - message: Initial message - """ - type: Literal["message_start"] - message: Message - -class RawMessageDeltaEvent(BaseModel): - """ - Raw message delta event from SSE. - - Attributes: - type: Always "message_delta" - delta: Changed fields as dict - usage: Token usage update - """ - type: Literal["message_delta"] - delta: dict[str, Any] - usage: MessageDeltaUsage - -class RawMessageStopEvent(BaseModel): - """ - Raw message stop event from SSE. - - Attributes: - type: Always "message_stop" - """ - type: Literal["message_stop"] - -class RawContentBlockStartEvent(BaseModel): - """ - Raw content block start from SSE. - - Attributes: - type: Always "content_block_start" - index: Content block index - content_block: Initial content block as dict - """ - type: Literal["content_block_start"] - index: int - content_block: dict[str, Any] - -class RawContentBlockDeltaEvent(BaseModel): - """ - Raw content block delta from SSE. - - Attributes: - type: Always "content_block_delta" - index: Content block index - delta: Delta content as dict - """ - type: Literal["content_block_delta"] - index: int - delta: dict[str, Any] - -class RawContentBlockStopEvent(BaseModel): - """ - Raw content block stop from SSE. - - Attributes: - type: Always "content_block_stop" - index: Content block index - """ - type: Literal["content_block_stop"] - index: int -``` - -## Pagination Types - -### Synchronous Pagination - -```python { .api } -class SyncPage(Generic[T]): - """ - Synchronous ID-based pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __iter__(self) -> Iterator[T]: - """Iterate over all items across pages automatically.""" - ... - - def __getitem__(self, index: int) -> T: - """Get item by index in current page.""" - ... - - def has_next_page(self) -> bool: - """Check if another page exists.""" - ... - - def next_page_info(self) -> dict[str, Any]: - """Get pagination parameters for next page.""" - ... - - def get_next_page(self) -> SyncPage[T]: - """Fetch next page.""" - ... - -class SyncTokenPage(Generic[T]): - """ - Synchronous token-based pagination. - - Uses continuation tokens instead of IDs. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __iter__(self) -> Iterator[T]: ... - def has_next_page(self) -> bool: ... - def next_page_info(self) -> dict[str, Any]: ... - def get_next_page(self) -> SyncTokenPage[T]: ... - -class SyncPageCursor(Generic[T]): - """ - Synchronous cursor-based pagination. - - Uses cursors for pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __iter__(self) -> Iterator[T]: ... - def has_next_page(self) -> bool: ... - def next_page_info(self) -> dict[str, Any]: ... - def get_next_page(self) -> SyncPageCursor[T]: ... -``` - -### Asynchronous Pagination - -```python { .api } -class AsyncPage(Generic[T]): - """ - Asynchronous ID-based pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __aiter__(self) -> AsyncIterator[T]: - """Async iterate over all items across pages.""" - ... - - def __getitem__(self, index: int) -> T: - """Get item by index in current page.""" - ... - - async def has_next_page(self) -> bool: - """Check if another page exists.""" - ... - - async def next_page_info(self) -> dict[str, Any]: - """Get pagination parameters for next page.""" - ... - - async def get_next_page(self) -> AsyncPage[T]: - """Fetch next page.""" - ... - -class AsyncTokenPage(Generic[T]): - """ - Asynchronous token-based pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __aiter__(self) -> AsyncIterator[T]: ... - async def has_next_page(self) -> bool: ... - async def next_page_info(self) -> dict[str, Any]: ... - async def get_next_page(self) -> AsyncTokenPage[T]: ... - -class AsyncPageCursor(Generic[T]): - """ - Asynchronous cursor-based pagination. - - Attributes: - data: Items in current page - """ - data: list[T] - - def __aiter__(self) -> AsyncIterator[T]: ... - async def has_next_page(self) -> bool: ... - async def next_page_info(self) -> dict[str, Any]: ... - async def get_next_page(self) -> AsyncPageCursor[T]: ... -``` - -## Type Helpers - -```python { .api } -class NOT_GIVEN: - """ - Sentinel value for omitted optional parameters. - - Used to distinguish between explicitly passing None vs not passing a parameter. - This allows the SDK to differentiate between: - - Parameter not provided (NOT_GIVEN) - - Parameter explicitly set to None (None) - """ - ... - -NotGiven = type[NOT_GIVEN] - -def not_given() -> NOT_GIVEN: - """Return NOT_GIVEN sentinel.""" - ... - -class Omit: - """ - Type for omitted fields. - - Used internally for partial updates where some fields should be omitted. - """ - ... - -def omit() -> Omit: - """Return Omit sentinel.""" - ... - -NoneType = type[None] -``` - -## Request Parameter Types - -```python { .api } -class MessageCreateParams(TypedDict): - """ - Complete parameters for message creation. - - Fields: - model: Model identifier (required) - messages: Conversation messages (required) - max_tokens: Maximum tokens to generate (required) - system: System prompt (optional) - metadata: Request metadata (optional) - stop_sequences: Stop sequences (optional) - stream: Enable streaming (optional) - temperature: Sampling temperature 0.0-1.0 (optional) - top_p: Nucleus sampling (optional) - top_k: Top-k sampling (optional) - tools: Available tools (optional) - tool_choice: Tool selection control (optional) - service_tier: Service tier selection (optional) - thinking: Extended thinking configuration (optional) - """ - model: str - messages: list[MessageParam] - max_tokens: int - system: NotRequired[str | list[TextBlockParam]] - metadata: NotRequired[MetadataParam] - stop_sequences: NotRequired[list[str]] - stream: NotRequired[bool] - temperature: NotRequired[float] - top_p: NotRequired[float] - top_k: NotRequired[int] - tools: NotRequired[list[ToolParam]] - tool_choice: NotRequired[ToolChoice] - service_tier: NotRequired[Literal["auto", "standard_only"]] - thinking: NotRequired[ThinkingConfigParam] - -class RequestOptions(TypedDict, total=False): - """ - Per-request options. - - Fields: - headers: Custom headers - max_retries: Maximum retry attempts - timeout: Request timeout - query: Custom query parameters - """ - headers: dict[str, str] - max_retries: int - timeout: float | httpx.Timeout - query: dict[str, object] -``` - -## Response Wrapper Types - -```python { .api } -class APIResponse(Generic[T]): - """ - HTTP response wrapper with parsed data. - - Attributes: - data: Parsed response data - headers: Response headers - status_code: HTTP status code - request: Original request - """ - data: T - headers: dict[str, str] - status_code: int - request: httpx.Request - - def parse(self) -> T: - """ - Get parsed response data. - - Returns: - Parsed response object of type T - """ - ... - -class AsyncAPIResponse(Generic[T]): - """ - Async HTTP response wrapper. - - Attributes: - data: Parsed response data - headers: Response headers - status_code: HTTP status code - request: Original request - """ - data: T - headers: dict[str, str] - status_code: int - request: httpx.Request - - async def parse(self) -> T: - """ - Get parsed response data asynchronously. - - Returns: - Parsed response object of type T - """ - ... -``` - -## Base Model - -```python { .api } -class BaseModel(pydantic.BaseModel): - """ - Base Pydantic model for all response types. - - Provides: - - JSON serialization/deserialization - - Type validation - - Field access - - Model copying - - Schema generation - """ - def model_dump( - self, - *, - mode: Literal["json", "python"] = "python", - include: set[str] | None = None, - exclude: set[str] | None = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - ) -> dict[str, Any]: - """ - Convert model to dictionary. - - Parameters: - mode: Serialization mode ("json" or "python") - include: Fields to include - exclude: Fields to exclude - by_alias: Use field aliases - exclude_unset: Exclude fields that weren't set - exclude_defaults: Exclude fields with default values - exclude_none: Exclude fields with None values - - Returns: - Dictionary representation of model - """ - ... - - def model_dump_json( - self, - *, - indent: int | None = None, - include: set[str] | None = None, - exclude: set[str] | None = None, - by_alias: bool = False, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - ) -> str: - """ - Convert model to JSON string. - - Parameters: - indent: JSON indentation - include: Fields to include - exclude: Fields to exclude - by_alias: Use field aliases - exclude_unset: Exclude fields that weren't set - exclude_defaults: Exclude fields with default values - exclude_none: Exclude fields with None values - - Returns: - JSON string representation - """ - ... - - @classmethod - def model_validate(cls, obj: Any) -> Self: - """ - Validate and parse object into model. - - Parameters: - obj: Object to validate (dict, model instance, etc.) - - Returns: - Validated model instance - - Raises: - ValidationError: If validation fails - """ - ... - - @classmethod - def model_validate_json(cls, json_data: str | bytes) -> Self: - """ - Validate and parse JSON into model. - - Parameters: - json_data: JSON string or bytes - - Returns: - Validated model instance - - Raises: - ValidationError: If validation fails - """ - ... - - def model_copy( - self, - *, - update: dict[str, Any] | None = None, - deep: bool = False, - ) -> Self: - """ - Create copy of model with optional updates. - - Parameters: - update: Dictionary of fields to update - deep: Whether to make deep copy - - Returns: - New model instance - """ - ... - - @classmethod - def model_json_schema( - cls, - *, - by_alias: bool = True, - ref_template: str = "#/$defs/{model}", - ) -> dict[str, Any]: - """ - Generate JSON Schema for model. - - Parameters: - by_alias: Use field aliases in schema - ref_template: Template for $ref values - - Returns: - JSON Schema dictionary - """ - ... -``` - -## SDK Infrastructure Types - -### HTTP and Client Types - -```python { .api } -from typing import Union -import httpx - -Timeout = Union[float, httpx.Timeout, None] -""" -Timeout specification for HTTP requests. - -Can be: -- float: Total timeout in seconds -- httpx.Timeout: Granular timeout with connect/read/write/pool settings -- None: No timeout (not recommended) -""" - -Transport = Union[httpx.HTTPTransport, httpx.AsyncHTTPTransport] -"""HTTP transport for custom connection pooling and proxying.""" - -ProxiesTypes = Union[str, httpx.Proxy, dict[str, Union[str, httpx.Proxy]]] -""" -Proxy configuration types. - -Can be: -- str: Proxy URL -- httpx.Proxy: Configured proxy object -- dict: Mapping of protocols to proxy URLs -""" - -FileTypes = tuple[str, bytes, str] -""" -File tuple for upload operations. - -Format: (filename, content, mime_type) - -Example: - ("document.pdf", pdf_bytes, "application/pdf") -""" -``` - -### Sentinel Types - -```python { .api } -class NotGivenType: - """Singleton type for NOT_GIVEN sentinel value.""" - ... - -NotGiven = NotGivenType -""" -Sentinel value indicating a parameter was not provided. - -Used to distinguish between None (explicitly passed) and -parameter not passed at all. - -Example: - def create(*, param: str | None | NotGiven = NOT_GIVEN): - if param is NOT_GIVEN: - # Parameter not provided - elif param is None: - # Parameter explicitly set to None -""" - -NOT_GIVEN: NotGiven -"""Singleton instance of NotGiven sentinel.""" -``` - -### Response Wrapper Types - -```python { .api } -from typing import Generic, TypeVar - -T = TypeVar('T') - -class APIResponse(Generic[T]): - """ - Wrapper providing access to raw HTTP response and parsed data. - - Attributes: - http_response: Raw httpx.Response object - data: Cached parsed response data - """ - http_response: httpx.Response - - def parse(self) -> T: - """ - Parse and return typed response data. - - Returns: - Parsed response object of type T - """ - ... - -class AsyncAPIResponse(Generic[T]): - """ - Async version of APIResponse. - - Attributes: - http_response: Raw httpx.Response object - data: Cached parsed response data - """ - http_response: httpx.Response - - async def parse(self) -> T: - """ - Async parse and return typed response data. - - Returns: - Parsed response object of type T - """ - ... -``` - -### Base Model - -```python { .api } -from pydantic import BaseModel as PydanticBaseModel - -class BaseModel(PydanticBaseModel): - """ - Base Pydantic model with SDK-specific configuration. - - All response types inherit from this class, providing: - - JSON serialization via .model_dump() and .model_dump_json() - - Type validation - - Field aliases and exclusions - """ - - def model_dump(self, **kwargs) -> dict[str, Any]: - """Serialize model to dictionary.""" - ... - - def model_dump_json(self, **kwargs) -> str: - """Serialize model to JSON string.""" - ... - - @classmethod - def model_validate(cls, obj: Any) -> Self: - """Validate and construct model from dict.""" - ... - - @classmethod - def model_json_schema(cls, **kwargs) -> dict[str, Any]: - """Generate JSON Schema for model.""" - ... -``` - -## See Also - -- [Messages API](../api/messages.md) - Core message types in use -- [Streaming API](../api/streaming.md) - Stream event types in use -- [Tool Use API](../api/tools.md) - Tool types in use -- [Batches API](../api/batches.md) - Batch types in use -- [Error Handling](./errors.md) - Error types and exception hierarchy -- [Client Configuration](./client-config.md) - Usage of Timeout and Transport types -- [Utilities](./utilities.md) - Usage of FileTypes diff --git a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md b/.tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md deleted file mode 100644 index dde1fc69d..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/docs/reference/utilities.md +++ /dev/null @@ -1,169 +0,0 @@ -# Utilities Reference - -Helper functions for file handling, schema transformation, and HTTP client customization. - -## File Utilities - -```python { .api } -def file_from_path(path: str | Path) -> FileTypes: - """ - Load single file from filesystem path. - - Parameters: - path: File path (string or Path object) - - Returns: - File-like object for API requests (tuple of filename, content, mime-type) - - Raises: - FileNotFoundError: If path does not exist - IsADirectoryError: If path is a directory - """ - ... - -def files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: - """ - Load all files from a directory recursively. - - Recursively walks through directory and loads all files, returning them - as a list suitable for batch file upload operations. - - Parameters: - directory: Directory path (string or PathLike object) - - Returns: - List of file objects, each as (filename, content, mime-type) tuple - - Raises: - FileNotFoundError: If directory does not exist - NotADirectoryError: If path is not a directory - """ - ... - -async def async_files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: - """ - Async version of files_from_dir(). - - Asynchronously loads all files from directory recursively. Useful for - large directories or when loading many files in async context. - - Parameters: - directory: Directory path (string or PathLike object) - - Returns: - List of file objects, each as (filename, content, mime-type) tuple - - Raises: - FileNotFoundError: If directory does not exist - NotADirectoryError: If path is not a directory - """ - ... -``` - -## Schema Transformation - -```python { .api } -def transform_schema(schema: dict[str, Any]) -> dict[str, Any]: - """ - Transform JSON schema for structured outputs. - - Parameters: - schema: JSON Schema dictionary - - Returns: - Transformed schema for API - """ - ... -``` - -## HTTP Client Factories - -```python { .api } -class DefaultHttpxClient: - """Default synchronous HTTP client.""" - def __init__( - self, - *, - proxy: str | httpx.Proxy | None = None, - transport: httpx.HTTPTransport | None = None, - **kwargs - ): ... - -class DefaultAsyncHttpxClient: - """Default asynchronous HTTP client.""" - def __init__( - self, - *, - proxy: str | httpx.Proxy | None = None, - transport: httpx.AsyncHTTPTransport | None = None, - **kwargs - ): ... - -class DefaultAioHttpClient: - """Alternative async HTTP client using aiohttp.""" - def __init__(self, **kwargs): ... -``` - -## Quick Examples - -### Load Single File - -```python -from anthropic._utils import file_from_path - -file = file_from_path("document.pdf") -uploaded = client.beta.files.upload(file, purpose="batch") -``` - -### Load Multiple Files from Directory - -```python -from anthropic._utils import files_from_dir - -# Load all files from a directory -files = files_from_dir("./documents") -print(f"Loaded {len(files)} files") - -# Upload all files for batch processing -for file in files: - uploaded = client.beta.files.upload(file, purpose="batch") - print(f"Uploaded: {uploaded.id}") -``` - -### Async Load Files from Directory - -```python -import asyncio -from anthropic._utils import async_files_from_dir - -async def upload_directory(): - client = AsyncAnthropic() - - # Asynchronously load all files - files = await async_files_from_dir("./documents") - - # Upload concurrently - tasks = [client.beta.files.upload(f, purpose="batch") for f in files] - results = await asyncio.gather(*tasks) - - print(f"Uploaded {len(results)} files") - -asyncio.run(upload_directory()) -``` - -### Transform Schema - -```python -from pydantic import BaseModel -from anthropic.lib._parse._transform import transform_schema - -class Response(BaseModel): - answer: str - confidence: float - -schema = transform_schema(Response.model_json_schema()) -``` - -## See Also - -- [Client Configuration](./client-config.md) - Client initialization diff --git a/.tessl/tiles/tessl/pypi-anthropic/tile.json b/.tessl/tiles/tessl/pypi-anthropic/tile.json deleted file mode 100644 index e86f85ede..000000000 --- a/.tessl/tiles/tessl/pypi-anthropic/tile.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "tessl/pypi-anthropic", - "version": "0.75.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/anthropic@0.75.0", - "summary": "The official Python library for the anthropic API", - "private": false -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/configuration.md b/.tessl/tiles/tessl/pypi-dill/docs/configuration.md deleted file mode 100644 index b7594884d..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/configuration.md +++ /dev/null @@ -1,636 +0,0 @@ -# Configuration and Settings - -dill provides global configuration options and settings that control serialization behavior, protocol selection, and various operational modes for optimal performance and compatibility. - -## Global Settings - -### Settings Dictionary - -```python { .api } -# Global settings dictionary -settings = { - 'protocol': DEFAULT_PROTOCOL, # int: Default pickle protocol version - 'byref': False, # bool: Pickle by reference when possible - 'fmode': 0, # int: File mode setting (0=HANDLE_FMODE, 1=CONTENTS_FMODE, 2=FILE_FMODE) - 'recurse': False, # bool: Recursively pickle nested objects - 'ignore': False # bool: Ignore certain errors during serialization -} -``` - -### Protocol Constants - -```python { .api } -DEFAULT_PROTOCOL = 3 # Default pickle protocol version -HIGHEST_PROTOCOL = 5 # Highest supported pickle protocol version - -# File mode constants -HANDLE_FMODE = 0 # Preserve file handles during serialization -CONTENTS_FMODE = 1 # Save file contents instead of handles -FILE_FMODE = 2 # Save file metadata and paths -``` - -### Extension Control - -```python { .api } -def extend(use_dill=True): - """ - Add or remove dill types to/from the pickle registry. - - Controls whether dill's extended types are available in the standard - pickle module's dispatch table for backward compatibility. - - Parameters: - - use_dill: bool, if True extend dispatch table with dill types, - if False revert to standard pickle types only - - Returns: - None - """ -``` - -## Usage Examples - -### Basic Configuration - -```python -import dill - -# Check current settings -print("Current settings:") -for key, value in dill.settings.items(): - print(f" {key}: {value}") - -# Modify settings -original_protocol = dill.settings['protocol'] -dill.settings['protocol'] = dill.HIGHEST_PROTOCOL -dill.settings['byref'] = True - -print(f"\\nChanged protocol from {original_protocol} to {dill.settings['protocol']}") -print(f"Enabled byref: {dill.settings['byref']}") - -# Test with new settings -def test_function(): - return "Hello with new settings!" - -serialized = dill.dumps(test_function) -restored = dill.loads(serialized) -print(f"Function result: {restored()}") - -# Restore original settings -dill.settings['protocol'] = original_protocol -dill.settings['byref'] = False -``` - -### Protocol Selection and Performance - -```python -import dill -import time - -def benchmark_protocols(obj, protocols=None): - """Benchmark different pickle protocols.""" - if protocols is None: - protocols = [0, 1, 2, 3, 4, dill.HIGHEST_PROTOCOL] - - results = {} - - for protocol in protocols: - try: - # Set protocol - original_protocol = dill.settings['protocol'] - dill.settings['protocol'] = protocol - - # Time serialization - start_time = time.time() - serialized = dill.dumps(obj) - serialize_time = time.time() - start_time - - # Time deserialization - start_time = time.time() - dill.loads(serialized) - deserialize_time = time.time() - start_time - - results[protocol] = { - 'serialize_time': serialize_time, - 'deserialize_time': deserialize_time, - 'size': len(serialized), - 'total_time': serialize_time + deserialize_time - } - - # Restore original protocol - dill.settings['protocol'] = original_protocol - - except Exception as e: - results[protocol] = {'error': str(e)} - - return results - -# Test with complex object -complex_obj = { - 'functions': [lambda x: x**i for i in range(5)], - 'data': list(range(1000)), - 'nested': {'level1': {'level2': [i*j for i in range(10) for j in range(10)]}} -} - -protocol_results = benchmark_protocols(complex_obj) - -print("Protocol Performance Comparison:") -print("-" * 50) -for protocol, result in protocol_results.items(): - if 'error' in result: - print(f"Protocol {protocol}: ERROR - {result['error']}") - else: - print(f"Protocol {protocol}:") - print(f" Serialize: {result['serialize_time']:.4f}s") - print(f" Deserialize: {result['deserialize_time']:.4f}s") - print(f" Size: {result['size']:,} bytes") - print(f" Total: {result['total_time']:.4f}s") -``` - -### File Mode Configuration - -```python -import dill -import tempfile -import io - -def demonstrate_file_modes(): - """Demonstrate different file mode behaviors.""" - - # Create object with file handle - temp_file = tempfile.NamedTemporaryFile(mode='w+', delete=False) - temp_file.write("Test data for file mode demonstration") - temp_file.seek(0) - - obj_with_file = { - 'file_handle': temp_file, - 'file_path': temp_file.name, - 'data': [1, 2, 3, 4, 5] - } - - # Test different file modes - modes = { - dill.HANDLE_FMODE: "HANDLE_FMODE (preserve handles)", - dill.CONTENTS_FMODE: "CONTENTS_FMODE (save contents)", - dill.FILE_FMODE: "FILE_FMODE (save metadata)" - } - - for fmode, description in modes.items(): - print(f"\\nTesting {description}:") - print("-" * 40) - - try: - # Set file mode - original_fmode = dill.settings['fmode'] - dill.settings['fmode'] = fmode - - # Serialize - serialized = dill.dumps(obj_with_file) - print(f"✓ Serialization successful ({len(serialized)} bytes)") - - # Deserialize - restored = dill.loads(serialized) - print(f"✓ Deserialization successful") - - # Test restored file behavior - if 'file_handle' in restored: - file_obj = restored['file_handle'] - if hasattr(file_obj, 'read'): - try: - content = file_obj.read() - print(f"✓ File content: {repr(content[:50])}") - except: - print("✗ File handle not functional") - else: - print("○ File handle replaced with alternative representation") - - # Restore original setting - dill.settings['fmode'] = original_fmode - - except Exception as e: - print(f"✗ Failed: {e}") - - # Cleanup - temp_file.close() - import os - os.unlink(temp_file.name) - -demonstrate_file_modes() -``` - -### Reference vs Value Serialization - -```python -import dill - -def demonstrate_byref_setting(): - """Demonstrate byref setting effects.""" - - # Create shared object - shared_list = [1, 2, 3, 4, 5] - - # Create objects that reference the shared list - obj1 = {'data': shared_list, 'name': 'object1'} - obj2 = {'data': shared_list, 'name': 'object2'} - container = {'obj1': obj1, 'obj2': obj2, 'shared': shared_list} - - # Test with byref=False (default) - print("Testing with byref=False (value serialization):") - dill.settings['byref'] = False - - serialized_value = dill.dumps(container) - restored_value = dill.loads(serialized_value) - - # Modify shared list in restored object - restored_value['shared'].append(999) - - print(f" Original obj1 data: {container['obj1']['data']}") - print(f" Restored obj1 data: {restored_value['obj1']['data']}") - print(f" Restored obj2 data: {restored_value['obj2']['data']}") - print(f" Objects share data in restored: {restored_value['obj1']['data'] is restored_value['obj2']['data']}") - - # Test with byref=True - print("\\nTesting with byref=True (reference serialization):") - dill.settings['byref'] = True - - serialized_ref = dill.dumps(container) - restored_ref = dill.loads(serialized_ref) - - # Modify shared list in restored object - restored_ref['shared'].append(888) - - print(f" Restored obj1 data: {restored_ref['obj1']['data']}") - print(f" Restored obj2 data: {restored_ref['obj2']['data']}") - print(f" Objects share data in restored: {restored_ref['obj1']['data'] is restored_ref['obj2']['data']}") - - print(f"\\nSerialized sizes:") - print(f" Value mode: {len(serialized_value)} bytes") - print(f" Reference mode: {len(serialized_ref)} bytes") - - # Restore default - dill.settings['byref'] = False - -demonstrate_byref_setting() -``` - -### Recursive Serialization Control - -```python -import dill - -def demonstrate_recursive_setting(): - """Demonstrate recursive serialization setting.""" - - # Create nested structure with functions - def outer_function(x): - def inner_function(y): - def innermost_function(z): - return x + y + z - return innermost_function - return inner_function - - nested_func = outer_function(10) - deeply_nested = nested_func(20) - - test_obj = { - 'outer': outer_function, - 'nested': nested_func, - 'deep': deeply_nested, - 'data': {'level1': {'level2': {'level3': [1, 2, 3]}}} - } - - # Test with recurse=False (default) - print("Testing with recurse=False:") - dill.settings['recurse'] = False - - try: - serialized_no_recurse = dill.dumps(test_obj) - restored_no_recurse = dill.loads(serialized_no_recurse) - - # Test functionality - result = restored_no_recurse['deep'](30) - print(f" ✓ Serialization successful, result: {result}") - print(f" Size: {len(serialized_no_recurse)} bytes") - except Exception as e: - print(f" ✗ Failed: {e}") - - # Test with recurse=True - print("\\nTesting with recurse=True:") - dill.settings['recurse'] = True - - try: - serialized_recurse = dill.dumps(test_obj) - restored_recurse = dill.loads(serialized_recurse) - - # Test functionality - result = restored_recurse['deep'](30) - print(f" ✓ Serialization successful, result: {result}") - print(f" Size: {len(serialized_recurse)} bytes") - except Exception as e: - print(f" ✗ Failed: {e}") - - # Restore default - dill.settings['recurse'] = False - -demonstrate_recursive_setting() -``` - -## Advanced Configuration Management - -### Configuration Context Manager - -```python -import dill -from contextlib import contextmanager - -@contextmanager -def dill_config(**settings): - """Context manager for temporary dill configuration.""" - # Save original settings - original = dill.settings.copy() - - # Apply new settings - dill.settings.update(settings) - - try: - yield dill.settings - finally: - # Restore original settings - dill.settings.clear() - dill.settings.update(original) - -# Usage examples -def test_function(): - return "Test result" - -# Temporary high-performance configuration -with dill_config(protocol=dill.HIGHEST_PROTOCOL, byref=True): - print(f"Using protocol: {dill.settings['protocol']}") - serialized = dill.dumps(test_function) - print(f"Serialized size: {len(serialized)} bytes") - -# Back to default settings -print(f"Back to protocol: {dill.settings['protocol']}") - -# Temporary memory-efficient configuration -with dill_config(fmode=dill.CONTENTS_FMODE, recurse=True): - complex_obj = {'data': [1, 2, 3], 'func': lambda x: x*2} - serialized = dill.dumps(complex_obj) - restored = dill.loads(serialized) - print(f"Function result: {restored['func'](5)}") -``` - -### Configuration Profiles - -```python -import dill - -class DillProfile: - """Predefined configuration profiles for different use cases.""" - - # Performance-optimized profile - PERFORMANCE = { - 'protocol': dill.HIGHEST_PROTOCOL, - 'byref': False, - 'fmode': dill.HANDLE_FMODE, - 'recurse': False, - 'ignore': False - } - - # Size-optimized profile - COMPACT = { - 'protocol': dill.HIGHEST_PROTOCOL, - 'byref': True, - 'fmode': dill.CONTENTS_FMODE, - 'recurse': True, - 'ignore': False - } - - # Compatibility profile - COMPATIBLE = { - 'protocol': 2, # Widely supported protocol - 'byref': False, - 'fmode': dill.CONTENTS_FMODE, - 'recurse': False, - 'ignore': True - } - - # Debug profile - DEBUG = { - 'protocol': 0, # Human-readable protocol - 'byref': False, - 'fmode': dill.CONTENTS_FMODE, - 'recurse': True, - 'ignore': False - } - -class ConfigManager: - """Configuration management utility.""" - - def __init__(self): - self.saved_configs = {} - self.current_profile = None - - def apply_profile(self, profile_name): - """Apply a predefined profile.""" - profiles = { - 'performance': DillProfile.PERFORMANCE, - 'compact': DillProfile.COMPACT, - 'compatible': DillProfile.COMPATIBLE, - 'debug': DillProfile.DEBUG - } - - if profile_name in profiles: - # Save current config - self.save_config('pre_profile') - - # Apply profile - dill.settings.update(profiles[profile_name]) - self.current_profile = profile_name - - print(f"Applied {profile_name} profile:") - for key, value in profiles[profile_name].items(): - print(f" {key}: {value}") - else: - print(f"Unknown profile: {profile_name}") - - def save_config(self, name): - """Save current configuration.""" - self.saved_configs[name] = dill.settings.copy() - print(f"Saved configuration as '{name}'") - - def restore_config(self, name): - """Restore saved configuration.""" - if name in self.saved_configs: - dill.settings.clear() - dill.settings.update(self.saved_configs[name]) - self.current_profile = None - print(f"Restored configuration '{name}'") - else: - print(f"No saved configuration named '{name}'") - - def list_configs(self): - """List saved configurations.""" - print("Saved configurations:") - for name in self.saved_configs: - print(f" - {name}") - - def show_current_config(self): - """Show current configuration.""" - print("Current configuration:") - for key, value in dill.settings.items(): - print(f" {key}: {value}") - if self.current_profile: - print(f"Profile: {self.current_profile}") - -# Usage example -config_manager = ConfigManager() - -# Test different profiles -test_obj = { - 'function': lambda x: x**2, - 'data': list(range(100)), - 'nested': {'level1': {'level2': 'deep_value'}} -} - -profiles_to_test = ['performance', 'compact', 'compatible', 'debug'] - -for profile in profiles_to_test: - print(f"\\n{'='*50}") - print(f"Testing {profile.upper()} profile") - print('='*50) - - config_manager.apply_profile(profile) - - try: - serialized = dill.dumps(test_obj) - restored = dill.loads(serialized) - - print(f"✓ Serialization successful") - print(f" Size: {len(serialized):,} bytes") - print(f" Function test: {restored['function'](5)}") - - except Exception as e: - print(f"✗ Failed: {e}") - -# Restore original configuration -config_manager.restore_config('pre_profile') -``` - -### Environment-Specific Configuration - -```python -import dill -import os -import platform -import sys - -class EnvironmentConfig: - """Automatically configure dill based on environment.""" - - @staticmethod - def auto_configure(): - """Automatically configure based on environment.""" - config = {} - - # Python version considerations - if sys.version_info >= (3, 8): - config['protocol'] = dill.HIGHEST_PROTOCOL - else: - config['protocol'] = 3 # Safe for older versions - - # Platform considerations - if platform.system() == 'Windows': - config['fmode'] = dill.CONTENTS_FMODE # Better Windows compatibility - else: - config['fmode'] = dill.HANDLE_FMODE - - # Memory considerations - import psutil - available_memory = psutil.virtual_memory().available - if available_memory < 1024**3: # Less than 1GB - config['byref'] = True # Save memory - config['recurse'] = False - else: - config['byref'] = False - config['recurse'] = True - - # Development vs production - if os.environ.get('DILL_DEBUG'): - config['protocol'] = 0 # Human-readable - config['ignore'] = False # Strict error handling - elif os.environ.get('DILL_PRODUCTION'): - config['ignore'] = True # Lenient error handling - - # Apply configuration - dill.settings.update(config) - - print("Auto-configured dill based on environment:") - for key, value in config.items(): - print(f" {key}: {value}") - - return config - - @staticmethod - def validate_config(): - """Validate current configuration.""" - issues = [] - - # Check protocol compatibility - if dill.settings['protocol'] > 4 and sys.version_info < (3, 8): - issues.append("Protocol 5 requires Python 3.8+") - - # Check memory usage with byref setting - if not dill.settings['byref'] and dill.settings['recurse']: - issues.append("High memory usage: recurse=True with byref=False") - - # Check file mode compatibility - if dill.settings['fmode'] == dill.HANDLE_FMODE and platform.system() == 'Windows': - issues.append("HANDLE_FMODE may have issues on Windows") - - if issues: - print("Configuration issues found:") - for issue in issues: - print(f" ⚠ {issue}") - else: - print("✓ Configuration validation passed") - - return len(issues) == 0 - -# Example usage -print("Current system information:") -print(f" Python: {sys.version}") -print(f" Platform: {platform.system()}") -print(f" Memory: {psutil.virtual_memory().total / (1024**3):.1f}GB") - -# Auto-configure -env_config = EnvironmentConfig() -env_config.auto_configure() - -# Validate configuration -env_config.validate_config() - -# Test with auto-configuration -test_function = lambda x: x * 2 + 1 -serialized = dill.dumps(test_function) -restored = dill.loads(serialized) -print(f"\\nTest result: {restored(10)}") -print(f"Serialized size: {len(serialized)} bytes") -``` - -## Best Practices - -### Configuration Management Guidelines - -1. **Profile Usage**: Use predefined profiles for common scenarios -2. **Context Managers**: Use context managers for temporary configuration changes -3. **Environment Awareness**: Configure based on deployment environment -4. **Performance Testing**: Benchmark different configurations for your use case -5. **Documentation**: Document configuration choices and their rationale - -### Performance Considerations - -1. **Protocol Selection**: Higher protocols generally offer better performance -2. **Memory vs Speed**: `byref=True` saves memory but may slow deserialization -3. **File Mode Impact**: Choose file mode based on your file handling needs -4. **Recursive Overhead**: Enable recursion only when necessary for complex objects \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/core-serialization.md b/.tessl/tiles/tessl/pypi-dill/docs/core-serialization.md deleted file mode 100644 index 130a9d6b6..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/core-serialization.md +++ /dev/null @@ -1,285 +0,0 @@ -# Core Serialization - -dill's core serialization functions provide extended pickle functionality that can handle complex Python objects including functions, classes, lambdas, and nested structures that standard pickle cannot serialize. - -## Primary Serialization Functions - -### File-based Operations - -```python { .api } -def dump(obj, file, protocol=None, byref=None, fmode=None, recurse=None, **kwds): - """ - Serialize object to a file. - - Provides enhanced serialization capabilities beyond standard pickle.dump, - supporting complex objects like functions, classes, and nested structures. - - Parameters: - - obj: object to serialize - - file: file-like object to write to (must have write() method) - - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) - - byref: bool, pickle by reference when possible to reduce file size - - fmode: int, file mode for handle management (0=HANDLE_FMODE, 1=CONTENTS_FMODE, 2=FILE_FMODE) - - recurse: bool, recursively pickle nested objects and their dependencies - - **kwds: additional keyword arguments passed to Pickler - - Returns: - None - - Raises: - - PicklingError: when object cannot be serialized - - IOError: when file operations fail - """ - -def load(file, ignore=None, **kwds): - """ - Deserialize object from a file. - - Provides enhanced deserialization capabilities beyond standard pickle.load, - with improved error handling and support for complex object restoration. - - Parameters: - - file: file-like object to read from (must have read() method) - - ignore: bool, ignore certain unpickling errors and continue - - **kwds: additional keyword arguments passed to Unpickler - - Returns: - object: deserialized object with full functionality restored - - Raises: - - UnpicklingError: when deserialization fails - - IOError: when file operations fail - """ -``` - -### String-based Operations - -```python { .api } -def dumps(obj, protocol=None, byref=None, fmode=None, recurse=None, **kwds): - """ - Serialize object to a bytes string. - - Converts any Python object to a bytes representation that can be - stored, transmitted, or restored later with full functionality. - - Parameters: - - obj: object to serialize (any Python object) - - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) - - byref: bool, pickle by reference when possible to reduce size - - fmode: int, file mode for handle management - - recurse: bool, recursively pickle nested objects and dependencies - - **kwds: additional keyword arguments passed to Pickler - - Returns: - bytes: serialized object as bytes string - - Raises: - - PicklingError: when object cannot be serialized - """ - -def loads(str, ignore=None, **kwds): - """ - Deserialize object from a bytes string. - - Restores a Python object from its bytes representation with - full functionality and state preservation. - - Parameters: - - str: bytes, bytes string containing serialized object - - ignore: bool, ignore certain unpickling errors and continue - - **kwds: additional keyword arguments passed to Unpickler - - Returns: - object: deserialized object with original functionality - - Raises: - - UnpicklingError: when deserialization fails - - TypeError: when input is not bytes - """ -``` - -### Deep Copy Operation - -```python { .api } -def copy(obj, *args, **kwds): - """ - Create a deep copy of an object using serialization. - - Uses dill's enhanced serialization to create deep copies of complex - objects that standard copy.deepcopy cannot handle. - - Parameters: - - obj: object to copy (any Python object) - - *args: positional arguments passed to dumps/loads - - **kwds: keyword arguments passed to dumps/loads - - Returns: - object: deep copy of the input object with independent state - - Raises: - - PicklingError: when object cannot be serialized for copying - """ -``` - -## Usage Examples - -### Basic Function Serialization - -```python -import dill - -# Serialize a function with closure -def create_multiplier(factor): - def multiply(x): - return x * factor - return multiply - -# Create function with closure -times_three = create_multiplier(3) - -# Serialize to bytes -serialized = dill.dumps(times_three) - -# Deserialize and use -restored_function = dill.loads(serialized) -result = restored_function(4) # Returns 12 -``` - -### Class and Instance Serialization - -```python -import dill - -# Define a class with methods -class Calculator: - def __init__(self, precision=2): - self.precision = precision - - def add(self, a, b): - return round(a + b, self.precision) - -# Create instance -calc = Calculator(precision=3) - -# Serialize to file -with open('calculator.pkl', 'wb') as f: - dill.dump(calc, f) - -# Load from file -with open('calculator.pkl', 'rb') as f: - restored_calc = dill.load(f) - -result = restored_calc.add(1.2345, 2.6789) # Returns 3.913 -``` - -### Lambda and Nested Function Handling - -```python -import dill - -# Complex nested structure with lambdas -operations = { - 'square': lambda x: x * x, - 'cube': lambda x: x ** 3, - 'factorial': lambda n: 1 if n <= 1 else n * operations['factorial'](n-1) -} - -# Serialize complex structure -data = dill.dumps(operations) - -# Restore and use -restored_ops = dill.loads(data) -print(restored_ops['square'](5)) # 25 -print(restored_ops['factorial'](5)) # 120 -``` - -### Error Handling - -```python -import dill -from dill import PicklingError, UnpicklingError - -# Handle serialization errors -def safe_serialize(obj): - try: - return dill.dumps(obj) - except PicklingError as e: - print(f"Cannot serialize object: {e}") - return None - -# Handle deserialization errors -def safe_deserialize(data): - try: - return dill.loads(data) - except UnpicklingError as e: - print(f"Cannot deserialize data: {e}") - return None -``` - -## Advanced Features - -### Protocol Selection - -```python -import dill - -# Use specific protocol version -data = dill.dumps(my_object, protocol=dill.HIGHEST_PROTOCOL) - -# Check protocol compatibility -print(f"Default protocol: {dill.DEFAULT_PROTOCOL}") -print(f"Highest protocol: {dill.HIGHEST_PROTOCOL}") -``` - -### File Mode Options - -```python -import dill - -# Different file modes for handle management -obj = complex_object_with_files - -# Handle mode - preserve file handles -dill.dump(obj, file, fmode=dill.HANDLE_FMODE) - -# Contents mode - save file contents -dill.dump(obj, file, fmode=dill.CONTENTS_FMODE) - -# File mode - save file metadata -dill.dump(obj, file, fmode=dill.FILE_FMODE) -``` - -### Recursive Serialization - -```python -import dill - -# Enable recursive pickling for nested dependencies -nested_structure = create_complex_nested_object() - -# Recursively serialize all dependencies -data = dill.dumps(nested_structure, recurse=True) -``` - -## Integration with Standard Library - -dill functions as a drop-in replacement for pickle: - -```python -# Replace pickle with dill -import dill as pickle - -# All pickle functionality works -data = pickle.dumps(obj) -restored = pickle.loads(data) - -# Plus extended capabilities -function_data = pickle.dumps(my_function) # Works with dill, fails with pickle -``` - -## Performance Considerations - -- **Protocol Selection**: Higher protocols generally provide better performance and smaller file sizes -- **Recursive Mode**: Use `recurse=True` cautiously as it can significantly increase serialization time for large object graphs -- **File vs String**: File-based operations are more memory-efficient for large objects -- **Reference Mode**: `byref=True` can reduce file size but may affect object independence after deserialization \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md b/.tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md deleted file mode 100644 index 5a877ca22..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/diagnostic-tools.md +++ /dev/null @@ -1,467 +0,0 @@ -# Diagnostic Tools - -dill provides comprehensive diagnostic tools for analyzing serialization capabilities, identifying problematic objects, and debugging pickling issues with detailed error reporting and analysis. - -## Core Diagnostic Functions - -### Pickling Capability Testing - -```python { .api } -def pickles(obj, exact=False, safe=False, **kwds): - """ - Check if an object can be pickled. - - Tests whether an object can be successfully serialized and deserialized - using dill, providing a simple boolean result for compatibility checking. - - Parameters: - - obj: object to test for pickling capability - - exact: bool, use exact type matching for compatibility testing - - safe: bool, use safe mode to avoid side effects during testing - - **kwds: additional keyword arguments passed to dumps/loads - - Returns: - bool: True if object can be pickled and unpickled successfully - - Raises: - - Exception: only when safe=False and testing encounters severe errors - """ - -def check(obj, *args, **kwds): - """ - Check for pickling errors and print diagnostic information. - - Performs comprehensive pickling analysis and displays detailed - information about any issues encountered during serialization testing. - - Parameters: - - obj: object to check for pickling errors - - *args: positional arguments passed to pickles() - - **kwds: keyword arguments passed to pickles() - - Returns: - bool: True if no errors found, False if pickling issues detected - """ -``` - -### Problem Object Detection - -```python { .api } -def baditems(obj, exact=False, safe=False): - """ - Find objects that cannot be pickled within a complex structure. - - Recursively analyzes an object to identify specific items that - prevent successful serialization, helping isolate problematic components. - - Parameters: - - obj: object to analyze for unpickleable items - - exact: bool, use exact type matching for analysis - - safe: bool, use safe mode to avoid side effects - - Returns: - list: list of unpickleable objects found in the structure - """ - -def badobjects(obj, depth=0, exact=False, safe=False): - """ - Get objects that fail to pickle. - - Analyzes object structure at specific depth levels to identify - problematic objects that prevent serialization at different nesting levels. - - Parameters: - - obj: object to analyze - - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) - - exact: bool, use exact type matching - - safe: bool, use safe mode to avoid side effects - - Returns: - object or dict: at depth=0 returns the object if it fails to pickle (None if it pickles), - at depth>0 returns dict mapping attribute names to bad objects - """ - -def badtypes(obj, depth=0, exact=False, safe=False): - """ - Get types for objects that fail to pickle. - - Identifies specific types that cause pickling failures, providing - type-level analysis of serialization compatibility issues. - - Parameters: - - obj: object to analyze for problematic types - - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) - - exact: bool, use exact type matching - - safe: bool, use safe mode - - Returns: - type or dict: at depth=0 returns the type if object fails to pickle (None if it pickles), - at depth>0 returns dict mapping attribute names to problematic types - """ - -def errors(obj, depth=0, exact=False, safe=False): - """ - Get detailed pickling error information. - - Provides comprehensive error analysis including specific error messages, - problematic objects, and suggested solutions for pickling failures. - - Parameters: - - obj: object to analyze for errors - - depth: int, maximum analysis depth (0 for unlimited) - - exact: bool, use exact type matching - - safe: bool, use safe mode to avoid side effects - - Returns: - list: list of detailed error descriptions and diagnostic information - """ -``` - -## Usage Examples - -### Basic Compatibility Testing - -```python -import dill - -# Test various object types -def test_function(): - return "Hello from function" - -class TestClass: - def __init__(self, value): - self.value = value - - def method(self): - return self.value * 2 - -# Test individual objects -print(dill.pickles(test_function)) # True -print(dill.pickles(TestClass)) # True -print(dill.pickles(TestClass(42))) # True - -# Test problematic objects -import threading -lock = threading.Lock() -print(dill.pickles(lock)) # False (locks can't be pickled) - -# Use safe mode for testing -print(dill.pickles(dangerous_object, safe=True)) -``` - -### Comprehensive Error Analysis - -```python -import dill - -# Complex object with potential issues -class ComplexObject: - def __init__(self): - self.data = [1, 2, 3] - self.function = lambda x: x + 1 - self.lock = threading.Lock() # Problematic - self.nested = { - 'good': 'string', - 'bad': open('/dev/null', 'r') # File handle - problematic - } - -complex_obj = ComplexObject() - -# Quick check -if not dill.check(complex_obj): - print("Object has pickling issues") - - # Find problematic items - bad_items = dill.baditems(complex_obj) - print(f"Found {len(bad_items)} unpickleable items:") - for item in bad_items: - print(f" {type(item)}: {item}") - - # Get detailed error information - error_details = dill.errors(complex_obj) - print("\nDetailed errors:") - for error in error_details: - print(f" {error}") -``` - -### Automated Testing Suite - -```python -import dill - -def comprehensive_pickle_test(obj, name="object"): - """Comprehensive pickling analysis for an object.""" - print(f"\nTesting {name}...") - print("=" * 50) - - # Basic pickling test - can_pickle = dill.pickles(obj, safe=True) - print(f"Can pickle: {can_pickle}") - - if not can_pickle: - # Detailed analysis - print("\nProblematic items:") - bad_items = dill.baditems(obj, safe=True) - for i, item in enumerate(bad_items): - print(f" {i+1}. {type(item).__name__}: {repr(item)[:50]}...") - - print("\nProblematic types:") - bad_types = dill.badtypes(obj, safe=True) - for i, bad_type in enumerate(bad_types): - print(f" {i+1}. {bad_type}") - - print("\nError details:") - error_list = dill.errors(obj, safe=True) - for i, error in enumerate(error_list): - print(f" {i+1}. {error}") - - return can_pickle - -# Test various objects -test_objects = { - 'simple_function': lambda x: x + 1, - 'complex_class': ComplexObject(), - 'nested_structure': {'functions': [lambda x: x, lambda y: y**2]}, - 'problematic_object': complex_obj -} - -results = {} -for name, obj in test_objects.items(): - results[name] = comprehensive_pickle_test(obj, name) - -print(f"\nSummary: {sum(results.values())}/{len(results)} objects can be pickled") -``` - -### Interactive Debugging Session - -```python -import dill - -def debug_pickling_issues(obj): - """Interactive debugging for pickling issues.""" - print("Starting pickling diagnosis...") - - # Step 1: Basic test - if dill.pickles(obj): - print("✓ Object can be pickled successfully") - return True - - print("✗ Object cannot be pickled") - - # Step 2: Identify bad items - print("\nSearching for problematic items...") - bad_items = dill.baditems(obj) - - if not bad_items: - print("No specific bad items found - issue may be with object structure") - return False - - print(f"Found {len(bad_items)} problematic items:") - - # Step 3: Categorize issues - by_type = {} - for item in bad_items: - item_type = type(item).__name__ - if item_type not in by_type: - by_type[item_type] = [] - by_type[item_type].append(item) - - # Step 4: Provide solutions - for item_type, items in by_type.items(): - print(f"\n{item_type} objects ({len(items)} found):") - for i, item in enumerate(items[:3]): # Show first 3 - print(f" {i+1}. {repr(item)[:60]}...") - - # Suggest solutions - if item_type == 'Lock': - print(" → Solution: Remove locks or replace with serializable alternatives") - elif item_type == 'TextIOWrapper': - print(" → Solution: Close files or use file paths instead of handles") - elif item_type == 'module': - print(" → Solution: Import modules by name rather than storing references") - else: - print(f" → Solution: Remove {item_type} objects or find serializable alternatives") - - return False - -# Usage -debug_pickling_issues(problematic_object) -``` - -## Advanced Diagnostic Techniques - -### Depth-Based Analysis - -```python -import dill - -def analyze_by_depth(obj, max_depth=5): - """Analyze object pickling issues by depth level.""" - print("Depth-based analysis:") - print("-" * 30) - - for depth in range(max_depth + 1): - bad_objects = dill.badobjects(obj, depth=depth, safe=True) - bad_types = dill.badtypes(obj, depth=depth, safe=True) - - print(f"Depth {depth}:") - print(f" Bad objects: {len(bad_objects)}") - print(f" Bad types: {len(set(t.__name__ for t in bad_types))}") - - if bad_objects: - # Show sample bad objects at this depth - sample = bad_objects[:2] - for obj in sample: - print(f" {type(obj).__name__}: {repr(obj)[:40]}...") - -# Usage -analyze_by_depth(complex_nested_object) -``` - -### Custom Diagnostic Rules - -```python -import dill - -class PicklingDiagnostic: - """Custom diagnostic tool for pickling analysis.""" - - def __init__(self): - self.rules = { - 'file_handles': lambda obj: hasattr(obj, 'read') and hasattr(obj, 'write'), - 'locks': lambda obj: hasattr(obj, 'acquire') and hasattr(obj, 'release'), - 'generators': lambda obj: hasattr(obj, '__next__') and hasattr(obj, '__iter__'), - 'lambda_functions': lambda obj: callable(obj) and obj.__name__ == '', - } - - def diagnose(self, obj): - """Run custom diagnostic rules.""" - issues = {} - - # Test overall pickling - can_pickle = dill.pickles(obj, safe=True) - issues['can_pickle'] = can_pickle - - if can_pickle: - return issues - - # Run custom rules - bad_items = dill.baditems(obj, safe=True) - - for rule_name, rule_func in self.rules.items(): - matching_items = [item for item in bad_items if rule_func(item)] - if matching_items: - issues[rule_name] = matching_items - - return issues - - def report(self, obj, name="object"): - """Generate diagnostic report.""" - issues = self.diagnose(obj) - - print(f"Diagnostic Report for {name}") - print("=" * 40) - - if issues.get('can_pickle', False): - print("✓ Object can be pickled successfully") - return - - print("✗ Object cannot be pickled") - print("\nIssues found:") - - for issue_type, items in issues.items(): - if issue_type == 'can_pickle': - continue - - print(f"\n{issue_type.replace('_', ' ').title()}:") - for item in items[:3]: # Show first 3 - print(f" - {type(item).__name__}: {repr(item)[:50]}...") - -# Usage -diagnostic = PicklingDiagnostic() -diagnostic.report(complex_object, "MyComplexObject") -``` - -### Performance Impact Analysis - -```python -import dill -import time - -def performance_diagnostic(obj, name="object"): - """Analyze performance impact of pickling.""" - print(f"Performance analysis for {name}") - print("-" * 40) - - # Test if object can be pickled - if not dill.pickles(obj, safe=True): - print("Object cannot be pickled - skipping performance test") - return - - # Measure serialization time - start_time = time.time() - serialized = dill.dumps(obj) - serialize_time = time.time() - start_time - - # Measure deserialization time - start_time = time.time() - dill.loads(serialized) - deserialize_time = time.time() - start_time - - # Report results - print(f"Serialization time: {serialize_time:.4f} seconds") - print(f"Deserialization time: {deserialize_time:.4f} seconds") - print(f"Serialized size: {len(serialized):,} bytes") - print(f"Compression ratio: {len(str(obj).encode()) / len(serialized):.2f}x") - -# Usage with different objects -performance_diagnostic(large_data_structure, "Large Data Structure") -performance_diagnostic(complex_function, "Complex Function") -performance_diagnostic(class_instance, "Class Instance") -``` - -## Integration with Development Workflow - -### Pre-commit Pickling Validation - -```python -import dill -import sys - -def validate_pickling(modules_to_test): - """Validate that key objects can be pickled before commit.""" - print("Running pickling validation...") - - failed_objects = [] - - for module_name in modules_to_test: - try: - module = __import__(module_name) - - # Test key objects in module - for attr_name in dir(module): - if not attr_name.startswith('_'): - obj = getattr(module, attr_name) - - if callable(obj) or hasattr(obj, '__dict__'): - if not dill.pickles(obj, safe=True): - failed_objects.append(f"{module_name}.{attr_name}") - - except ImportError: - print(f"Could not import {module_name}") - - if failed_objects: - print("❌ Pickling validation failed for:") - for obj in failed_objects: - print(f" - {obj}") - return False - else: - print("✓ All objects pass pickling validation") - return True - -# Usage in CI/CD pipeline -if __name__ == "__main__": - modules = ["mypackage.core", "mypackage.utils", "mypackage.models"] - success = validate_pickling(modules) - sys.exit(0 if success else 1) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/index.md b/.tessl/tiles/tessl/pypi-dill/docs/index.md deleted file mode 100644 index 7e9342e50..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/index.md +++ /dev/null @@ -1,453 +0,0 @@ -# dill - -dill is an extended Python serialization library that extends Python's pickle module to handle a broader range of Python objects including functions with yields, nested functions, lambdas, classes, and more exotic Python types. It provides a drop-in replacement for pickle with enhanced capabilities for serializing complex Python objects that standard pickle cannot handle, making it particularly useful for distributed computing, parallel processing, and saving interpreter sessions. - -The library offers additional features like source code inspection, interactive pickling diagnostics, and the ability to save and restore complete interpreter sessions, with applications in scientific computing, debugging, and development tools that require comprehensive Python object serialization. - -## Package Information - -- **Package Name**: dill -- **Language**: Python -- **Installation**: `pip install dill` -- **Optional Features**: `pip install dill[graph]` for object graph diagnostics, `pip install dill[profile]` for profiling tools - -## Core Imports - -```python -import dill -``` - -Common usage patterns: - -```python -import dill as pickle # Drop-in replacement for pickle -from dill import dump, dumps, load, loads # Direct function imports -``` - -## Basic Usage - -```python -import dill - -# Basic serialization - works like pickle but handles more types -def example_function(): - return "Hello from dill!" - -# Serialize function to bytes -serialized = dill.dumps(example_function) - -# Deserialize function -restored_function = dill.loads(serialized) -result = restored_function() # "Hello from dill!" - -# Serialize to file -with open('function.pkl', 'wb') as f: - dill.dump(example_function, f) - -# Load from file -with open('function.pkl', 'rb') as f: - loaded_function = dill.load(f) -``` - -## Architecture - -dill is designed as an extended version of Python's pickle module, maintaining full compatibility while adding support for previously unpickleable objects. The architecture consists of: - -- **Core Serialization Engine**: Extended pickler/unpickler classes that handle complex Python objects -- **Type Registry System**: Automatic detection and registration of new types for serialization -- **Session Management**: Capability to save and restore entire interpreter sessions including modules -- **Source Code Analysis**: Tools for inspecting and extracting source code from objects -- **Diagnostic Framework**: Utilities for identifying and debugging serialization issues - -dill integrates seamlessly with the broader Python ecosystem, supporting distributed computing frameworks like multiprocessing, concurrent.futures, and third-party libraries like Celery and Dask. - -## Capabilities - -### Core Serialization - -Primary serialization and deserialization functions that extend pickle's capabilities to handle complex Python objects including functions, classes, and nested structures. - -```python { .api } -def dump(obj, file, protocol=None, byref=None, fmode=None, recurse=None, **kwds): - """ - Serialize object to a file. - - Parameters: - - obj: object to serialize - - file: file-like object to write to - - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) - - byref: bool, pickle by reference when possible - - fmode: int, file mode for handle management - - recurse: bool, recursively pickle nested objects - - **kwds: additional keyword arguments - - Returns: - None - """ - -def dumps(obj, protocol=None, byref=None, fmode=None, recurse=None, **kwds): - """ - Serialize object to a bytes string. - - Parameters: - - obj: object to serialize - - protocol: int, pickle protocol version (default: DEFAULT_PROTOCOL) - - byref: bool, pickle by reference when possible - - fmode: int, file mode for handle management - - recurse: bool, recursively pickle nested objects - - **kwds: additional keyword arguments - - Returns: - bytes: serialized object as bytes string - """ - -def load(file, ignore=None, **kwds): - """ - Deserialize object from a file. - - Parameters: - - file: file-like object to read from - - ignore: bool, ignore certain unpickling errors - - **kwds: additional keyword arguments - - Returns: - object: deserialized object - """ - -def loads(str, ignore=None, **kwds): - """ - Deserialize object from a bytes string. - - Parameters: - - str: bytes string containing serialized object - - ignore: bool, ignore certain unpickling errors - - **kwds: additional keyword arguments - - Returns: - object: deserialized object - """ - -def copy(obj, *args, **kwds): - """ - Create a deep copy of an object using serialization. - - Parameters: - - obj: object to copy - - *args: positional arguments passed to dumps/loads - - **kwds: keyword arguments passed to dumps/loads - - Returns: - object: deep copy of the input object - """ -``` - -[Core Serialization](./core-serialization.md) - -### Extended Pickler Classes - -Enhanced Pickler and Unpickler classes that provide fine-grained control over the serialization process and support for complex Python objects. - -```python { .api } -class Pickler: - """ - Extended pickler with additional capabilities for complex objects. - - Provides enhanced serialization support beyond standard pickle.Pickler, - including functions, classes, and other previously unpickleable types. - """ - -class Unpickler: - """ - Extended unpickler with additional capabilities for complex objects. - - Provides enhanced deserialization support beyond standard pickle.Unpickler, - with improved error handling and type restoration. - """ -``` - -[Extended Pickler Classes](./pickler-classes.md) - -### Session Management - -Functions for saving and restoring complete interpreter sessions and individual modules, enabling persistence of development environments and interactive workflows. - -```python { .api } -def dump_session(filename=None, main=None, byref=False, **kwds): - """ - Save interpreter session to a file. - - Parameters: - - filename: str, output filename (default: temporary file) - - main: module, main module to save (default: __main__) - - byref: bool, pickle by reference when possible - - **kwds: additional keyword arguments - - Returns: - None - """ - -def load_session(filename=None, main=None, **kwds): - """ - Load interpreter session from a file. - - Parameters: - - filename: str, input filename - - main: module, target module (default: __main__) - - **kwds: additional keyword arguments - - Returns: - None - """ - -def dump_module(name=None, module=None, main=None, **kwds): - """ - Save a module to a file. - - Parameters: - - name: str, output filename - - module: module, module object to save - - main: module, main module context - - **kwds: additional keyword arguments - - Returns: - None - """ - -def load_module(name=None, module=None, main=None, **kwds): - """ - Load a module from a file. - - Parameters: - - name: str, input filename - - module: str, module name to load into - - main: module, main module context - - **kwds: additional keyword arguments - - Returns: - module: loaded module object - """ -``` - -[Session Management](./session-management.md) - -### Source Code Analysis - -Tools for extracting, analyzing, and manipulating source code from Python objects, enabling introspection and code generation capabilities. - -```python { .api } -def getsource(object, alias='', lstrip=False, enclosing=False, force=False, builtin=False): - """ - Get source code for an object. - - Parameters: - - object: object to get source for (module, class, method, function, traceback, frame, or code object) - - alias: str, alias name for the object (adds line of code that renames the object) - - lstrip: bool, ensure there is no indentation in the first line of code - - enclosing: bool, include enclosing code and dependencies - - force: bool, catch (TypeError,IOError) and try to use import hooks - - builtin: bool, force an import for any builtins - - Returns: - str: source code as single string - - Raises: - - IOError: when source code cannot be retrieved - - TypeError: for objects where source code is unavailable (e.g. builtins) - """ - -def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): - """ - Get the likely import string for the given object. - - Parameters: - - obj: object to inspect and generate import for - - alias: str, alias name to use (renames the object on import) - - verify: bool, test the import string before returning it - - builtin: bool, force an import for builtins where possible - - enclosing: bool, get the import for the outermost enclosing callable - - Returns: - str: import statement as string - """ -``` - -[Source Code Analysis](./source-analysis.md) - -### Diagnostic Tools - -Utilities for analyzing serialization capabilities, identifying problems, and debugging pickling issues with detailed error reporting. - -```python { .api } -def pickles(obj, exact=False, safe=False, **kwds): - """ - Check if an object can be pickled. - - Parameters: - - obj: object to test for pickling capability - - exact: bool, use exact type matching for compatibility testing - - safe: bool, use safe mode to avoid side effects during testing - - **kwds: additional keyword arguments passed to dumps/loads - - Returns: - bool: True if object can be pickled and unpickled successfully - """ - -def check(obj, *args, **kwds): - """ - Check for pickling errors and print diagnostic information. - - Parameters: - - obj: object to check for pickling errors - - *args: positional arguments passed to pickles() - - **kwds: keyword arguments passed to pickles() - - Returns: - bool: True if no errors found, False if pickling issues detected - """ - -def baditems(obj, exact=False, safe=False): - """ - Find objects that cannot be pickled within a complex structure. - - Parameters: - - obj: object to analyze for unpickleable items - - exact: bool, use exact type matching for analysis - - safe: bool, use safe mode to avoid side effects - - Returns: - list: list of unpickleable objects found in the structure - """ - -def badobjects(obj, depth=0, exact=False, safe=False): - """ - Get objects that fail to pickle. - - Parameters: - - obj: object to analyze - - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) - - exact: bool, use exact type matching - - safe: bool, use safe mode to avoid side effects - - Returns: - object or dict: at depth=0 returns the object if it fails to pickle (None if it pickles), - at depth>0 returns dict mapping attribute names to bad objects - """ - -def errors(obj, depth=0, exact=False, safe=False): - """ - Get detailed pickling error information. - - Parameters: - - obj: object to analyze for errors - - depth: int, analysis depth (0 for immediate object only, >0 for recursive analysis) - - exact: bool, use exact type matching - - safe: bool, use safe mode to avoid side effects - - Returns: - Exception or dict: detailed error information for pickling failures - """ -``` - -[Diagnostic Tools](./diagnostic-tools.md) - -### Type Registry System - -Functions for registering custom types and extending dill's serialization capabilities to handle new object types. - -```python { .api } -def register(t): - """ - Register a type with the pickler. - - Parameters: - - t: type, type to register - - Returns: - function: decorator function - """ - -def pickle(t, func): - """ - Add a type to the pickle dispatch table. - - Parameters: - - t: type, type to add - - func: function, pickling function for the type - - Returns: - None - """ - -def extend(use_dill=True): - """ - Add or remove dill types to/from the pickle registry. - - Parameters: - - use_dill: bool, if True extend dispatch table, if False revert - - Returns: - None - """ -``` - -[Type Registry System](./type-registry.md) - -### Temporary Operations - -Utilities for temporary file operations with serialization, stream capture, and IO buffer management for testing and development workflows. - -```python { .api } -def dump(object, **kwds): - """ - Dump object to a NamedTemporaryFile using dill.dump. - - Parameters: - - object: object to serialize to temporary file - - **kwds: optional keyword arguments including suffix, prefix, and NamedTemporaryFile options - - Returns: - file handle: NamedTemporaryFile handle containing serialized object - """ - -def load(file, **kwds): - """ - Load an object that was stored with dill.temp.dump. - - Parameters: - - file: file handle or str, file handle or path to file containing serialized object - - **kwds: optional keyword arguments including mode ('r' or 'rb', default: 'rb') - - Returns: - object: deserialized object - """ - -def capture(stream='stdout'): - """ - Capture stdout or stderr stream. - - Parameters: - - stream: str, stream name ('stdout' or 'stderr') - - Returns: - context manager for stream capture - """ -``` - -[Temporary Operations](./temp-operations.md) - -### Configuration and Settings - -Global configuration options and settings that control dill's behavior, protocol selection, and serialization modes. - -```python { .api } -# Global settings dictionary -settings = { - 'protocol': DEFAULT_PROTOCOL, # Default pickle protocol - 'byref': False, # Pickle by reference - 'fmode': 0, # File mode setting - 'recurse': False, # Recursive pickling - 'ignore': False # Ignore errors -} -``` - -[Configuration and Settings](./configuration.md) \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md b/.tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md deleted file mode 100644 index c5c937524..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/pickler-classes.md +++ /dev/null @@ -1,344 +0,0 @@ -# Extended Pickler Classes - -dill provides enhanced Pickler and Unpickler classes that extend Python's standard pickle classes with support for complex objects including functions, classes, lambdas, and other previously unpickleable types. - -## Enhanced Pickler Class - -```python { .api } -class Pickler: - """ - Extended pickler with additional capabilities for complex objects. - - Provides enhanced serialization support beyond standard pickle.Pickler, - including functions, classes, nested structures, and other complex types - that standard pickle cannot handle. - - Attributes: - - memo: dict, memoization cache for object references - - bin: bool, binary mode flag - - fast: bool, fast mode for performance optimization - - dispatch_table: dict, custom type dispatch table - - Methods: - - dump(obj): serialize object to file - - save(obj): internal save method - - persistent_id(obj): handle persistent object references - """ -``` - -## Enhanced Unpickler Class - -```python { .api } -class Unpickler: - """ - Extended unpickler with additional capabilities for complex objects. - - Provides enhanced deserialization support beyond standard pickle.Unpickler, - with improved error handling, type restoration, and support for complex - object reconstruction. - - Attributes: - - memo: dict, memoization cache for object reconstruction - - encoding: str, text encoding for string objects - - errors: str, error handling mode - - Methods: - - load(): deserialize object from file - - persistent_load(pid): handle persistent object loading - """ -``` - -## Usage Examples - -### Basic Pickler Usage - -```python -import dill -import io - -# Create a function with closure -def create_counter(start=0): - count = start - def increment(): - nonlocal count - count += 1 - return count - return increment - -counter = create_counter(10) - -# Use Pickler class directly -buffer = io.BytesIO() -pickler = dill.Pickler(buffer) -pickler.dump(counter) - -# Use Unpickler class directly -buffer.seek(0) -unpickler = dill.Unpickler(buffer) -restored_counter = unpickler.load() - -print(restored_counter()) # 11 -print(restored_counter()) # 12 -``` - -### Custom Protocol and Options - -```python -import dill -import io - -# Advanced pickler configuration -buffer = io.BytesIO() -pickler = dill.Pickler(buffer, protocol=dill.HIGHEST_PROTOCOL) - -# Configure pickler options through settings -original_settings = dill.settings.copy() -dill.settings['byref'] = True -dill.settings['recurse'] = True - -complex_object = create_complex_nested_structure() -pickler.dump(complex_object) - -# Restore original settings -dill.settings.update(original_settings) - -# Unpickle with custom error handling -buffer.seek(0) -unpickler = dill.Unpickler(buffer) -try: - restored_object = unpickler.load() -except dill.UnpicklingError as e: - print(f"Unpickling failed: {e}") -``` - -### Integration with File Objects - -```python -import dill - -# Direct file usage -with open('complex_data.pkl', 'wb') as f: - pickler = dill.Pickler(f) - pickler.dump(my_function) - pickler.dump(my_class) - pickler.dump(my_instance) - -# Load multiple objects -with open('complex_data.pkl', 'rb') as f: - unpickler = dill.Unpickler(f) - restored_function = unpickler.load() - restored_class = unpickler.load() - restored_instance = unpickler.load() -``` - -## Advanced Features - -### Custom Dispatch Tables - -```python -import dill -import io - -# Custom type handling -def save_custom_type(pickler, obj): - # Custom serialization logic - pickler.write(b'custom_marker') - pickler.save(obj.__dict__) - -# Create pickler with custom dispatch -buffer = io.BytesIO() -pickler = dill.Pickler(buffer) - -# Add custom type handler -if not hasattr(pickler, 'dispatch_table'): - pickler.dispatch_table = {} -pickler.dispatch_table[MyCustomType] = save_custom_type - -# Use with custom type -custom_obj = MyCustomType() -pickler.dump(custom_obj) -``` - -### Memory-Efficient Streaming - -```python -import dill - -def serialize_large_dataset(data_iterator, filename): - """Serialize large dataset in streaming fashion.""" - with open(filename, 'wb') as f: - pickler = dill.Pickler(f) - - # Stream objects one by one - for item in data_iterator: - pickler.dump(item) - -def deserialize_large_dataset(filename): - """Deserialize large dataset in streaming fashion.""" - with open(filename, 'rb') as f: - unpickler = dill.Unpickler(f) - - while True: - try: - item = unpickler.load() - yield item - except EOFError: - break - -# Usage -large_data = [complex_object(i) for i in range(10000)] -serialize_large_dataset(large_data, 'large_dataset.pkl') - -# Process items one by one without loading all into memory -for item in deserialize_large_dataset('large_dataset.pkl'): - process_item(item) -``` - -## Error Handling and Debugging - -```python -import dill -import io -from dill import PicklingError, UnpicklingError - -def safe_pickle_with_diagnostics(obj, buffer): - """Pickle with comprehensive error handling.""" - try: - pickler = dill.Pickler(buffer) - pickler.dump(obj) - return True - except PicklingError as e: - print(f"Pickling failed: {e}") - - # Use diagnostic tools - bad_items = dill.detect.baditems(obj) - if bad_items: - print("Unpickleable items found:") - for item in bad_items: - print(f" {item}") - - return False - except Exception as e: - print(f"Unexpected error: {e}") - return False - -def safe_unpickle_with_recovery(buffer): - """Unpickle with error recovery.""" - try: - buffer.seek(0) - unpickler = dill.Unpickler(buffer) - return unpickler.load() - except UnpicklingError as e: - print(f"Unpickling failed: {e}") - return None - except Exception as e: - print(f"Unexpected error during unpickling: {e}") - return None -``` - -## Performance Optimization - -```python -import dill -import io - -# Optimize for speed -def fast_pickle_config(): - """Configure dill for maximum speed.""" - # Use highest protocol - protocol = dill.HIGHEST_PROTOCOL - - # Configure for speed over size - settings = { - 'protocol': protocol, - 'byref': False, # Avoid reference resolution overhead - 'recurse': False # Avoid deep recursion overhead - } - - return settings - -# Optimize for size -def compact_pickle_config(): - """Configure dill for minimum size.""" - settings = { - 'protocol': dill.HIGHEST_PROTOCOL, - 'byref': True, # Use references to reduce duplication - 'recurse': True # Ensure complete object graphs - } - - return settings - -# Apply configuration -fast_settings = fast_pickle_config() -buffer = io.BytesIO() - -# Create optimized pickler -pickler = dill.Pickler(buffer, protocol=fast_settings['protocol']) - -# Apply settings through global configuration -original_settings = dill.settings.copy() -dill.settings.update(fast_settings) - -try: - pickler.dump(large_complex_object) -finally: - # Restore original settings - dill.settings.update(original_settings) -``` - -## Thread Safety Considerations - -```python -import dill -import threading -import io - -class ThreadSafePickler: - """Thread-safe wrapper for dill pickler operations.""" - - def __init__(self): - self._lock = threading.Lock() - - def dump_to_bytes(self, obj, **kwargs): - """Thread-safe serialization to bytes.""" - with self._lock: - return dill.dumps(obj, **kwargs) - - def load_from_bytes(self, data, **kwargs): - """Thread-safe deserialization from bytes.""" - with self._lock: - return dill.loads(data, **kwargs) - - def dump_to_file(self, obj, filename, **kwargs): - """Thread-safe serialization to file.""" - with self._lock: - with open(filename, 'wb') as f: - pickler = dill.Pickler(f) - pickler.dump(obj) - - def load_from_file(self, filename, **kwargs): - """Thread-safe deserialization from file.""" - with self._lock: - with open(filename, 'rb') as f: - unpickler = dill.Unpickler(f) - return unpickler.load() - -# Usage in multithreaded environment -safe_pickler = ThreadSafePickler() - -def worker_thread(obj, thread_id): - data = safe_pickler.dump_to_bytes(obj) - restored = safe_pickler.load_from_bytes(data) - print(f"Thread {thread_id}: Object successfully serialized and restored") - -# Start multiple threads -threads = [] -for i in range(5): - t = threading.Thread(target=worker_thread, args=(complex_object, i)) - threads.append(t) - t.start() - -# Wait for completion -for t in threads: - t.join() -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/session-management.md b/.tessl/tiles/tessl/pypi-dill/docs/session-management.md deleted file mode 100644 index e371a19db..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/session-management.md +++ /dev/null @@ -1,361 +0,0 @@ -# Session Management - -dill provides powerful capabilities for saving and restoring complete interpreter sessions and individual modules, enabling persistence of development environments, interactive workflows, and computational state. - -## Session Operations - -### Complete Session Management - -```python { .api } -def dump_session(filename=None, main=None, byref=False, **kwds): - """ - Save complete interpreter session to a file (deprecated). - - DEPRECATED: This function has been renamed to dump_module(). - Captures the entire state of the Python interpreter including all - variables, functions, classes, and imported modules from the main namespace. - - Parameters: - - filename: str, output filename (default: auto-generated in temp directory) - - main: module, main module to save (default: __main__ module) - - byref: bool, pickle objects by reference when possible - - **kwds: additional keyword arguments passed to dump_module - - Returns: - None - - Raises: - - PicklingError: when session cannot be serialized - - IOError: when file operations fail - """ - -def load_session(filename=None, main=None, **kwds): - """ - Load complete interpreter session from a file (deprecated). - - DEPRECATED: This function has been renamed to load_module(). - Restores the entire state of a Python interpreter session including - all variables, functions, classes, and module dependencies. - - Parameters: - - filename: str, input filename to load from - - main: module, target module to restore into (default: __main__) - - **kwds: additional keyword arguments passed to load_module - - Returns: - None (modifies current interpreter state) - - Raises: - - UnpicklingError: when session cannot be deserialized - - IOError: when file operations fail - """ -``` - -### Individual Module Management - -```python { .api } -def dump_module(filename=None, module=None, refimported=False, **kwds): - """ - Save a specific module to a file. - - Serializes a module's complete state including all attributes, - functions, classes, and internal variables while preserving - cross-module references. - - Parameters: - - filename: str or PathLike, output filename for module dump - - module: module or str, specific module object to save (default: __main__) - - refimported: bool, save imported modules by reference when possible - - **kwds: additional keyword arguments passed to Pickler - - Returns: - None - - Raises: - - PicklingError: when module cannot be serialized - - IOError: when file operations fail - """ - -def load_module(name=None, module=None, main=None, **kwds): - """ - Load a module from a file. - - Restores a module's complete state and integrates it into the - current interpreter environment with proper reference resolution. - - Parameters: - - name: str, input filename containing module dump - - module: str, name for the restored module in sys.modules - - main: module, main module context for integration - - **kwds: additional keyword arguments passed to Unpickler - - Returns: - module: loaded module object - - Raises: - - UnpicklingError: when module cannot be deserialized - - IOError: when file operations fail - """ - -def load_module_asdict(name=None, main=None, **kwds): - """ - Load a module from a file as a dictionary. - - Restores module contents as a dictionary rather than a module object, - useful for inspecting module contents without side effects. - - Parameters: - - name: str, input filename containing module dump - - main: module, main module context for reference resolution - - **kwds: additional keyword arguments passed to load_module - - Returns: - dict: module contents as dictionary - - Raises: - - UnpicklingError: when module cannot be deserialized - - IOError: when file operations fail - """ -``` - -## Usage Examples - -### Interactive Session Persistence - -```python -import dill - -# During interactive session, define some variables and functions -x = 42 -data = [1, 2, 3, 4, 5] - -def process_data(items): - return [item * 2 for item in items] - -class DataProcessor: - def __init__(self, multiplier=1): - self.multiplier = multiplier - - def process(self, data): - return [x * self.multiplier for x in data] - -processor = DataProcessor(3) - -# Save entire session -dill.dump_session('my_analysis_session.pkl') - -# Later, in a new Python session: -import dill -dill.load_session('my_analysis_session.pkl') - -# All variables and functions are now available -result = process_data(data) # [2, 4, 6, 8, 10] -processed = processor.process(data) # [3, 6, 9, 12, 15] -``` - -### Module-specific Operations - -```python -import dill -import mymodule - -# Save specific module -dill.dump_module('mymodule_backup.pkl', module=mymodule) - -# Modify module during development -mymodule.some_function = lambda x: x * 10 - -# Restore original module state -original_module = dill.load_module('mymodule_backup.pkl') -``` - -### Development Workflow Integration - -```python -import dill -import sys - -# Save current development state -def save_checkpoint(name): - """Save current session as a development checkpoint.""" - filename = f'checkpoint_{name}.pkl' - dill.dump_session(filename) - print(f"Session saved to {filename}") - -def load_checkpoint(name): - """Load a development checkpoint.""" - filename = f'checkpoint_{name}.pkl' - dill.load_session(filename) - print(f"Session loaded from {filename}") - -# Usage in development -save_checkpoint('before_experiment') - -# Run experiments, make changes... - -# Restore if needed -load_checkpoint('before_experiment') -``` - -### Module Inspection - -```python -import dill - -# Load module as dictionary for inspection -module_dict = dill.load_module_asdict('saved_module.pkl') - -# Inspect contents without side effects -print("Module contents:") -for name, obj in module_dict.items(): - if not name.startswith('_'): - print(f" {name}: {type(obj)}") - -# Selectively restore specific items -my_function = module_dict.get('my_function') -if my_function: - # Use the function without restoring entire module - result = my_function(input_data) -``` - -## Advanced Features - -### Session Filtering - -```python -import dill - -# Custom session dumping with filtering -def dump_filtered_session(filename, exclude_patterns=None): - """Dump session excluding certain patterns.""" - import __main__ - - # Get current main module - main_dict = __main__.__dict__.copy() - - # Filter out unwanted items - if exclude_patterns: - filtered_dict = {} - for name, obj in main_dict.items(): - if not any(pattern in name for pattern in exclude_patterns): - filtered_dict[name] = obj - - # Temporarily replace main dict - original_dict = __main__.__dict__ - __main__.__dict__ = filtered_dict - - try: - dill.dump_session(filename) - finally: - __main__.__dict__ = original_dict - else: - dill.dump_session(filename) - -# Usage -dump_filtered_session('clean_session.pkl', exclude_patterns=['_temp', 'debug_']) -``` - -### Cross-Environment Session Transfer - -```python -import dill -import sys -import os - -# Save session with environment info -def save_portable_session(filename): - """Save session with environment metadata.""" - # Save session - dill.dump_session(filename) - - # Save environment info - env_info = { - 'python_version': sys.version, - 'platform': sys.platform, - 'path': sys.path.copy(), - 'modules': list(sys.modules.keys()), - 'cwd': os.getcwd() - } - - env_filename = filename.replace('.pkl', '_env.pkl') - with open(env_filename, 'wb') as f: - dill.dump(env_info, f) - -def load_portable_session(filename): - """Load session with environment validation.""" - # Load environment info - env_filename = filename.replace('.pkl', '_env.pkl') - - try: - with open(env_filename, 'rb') as f: - env_info = dill.load(f) - - # Validate compatibility - if env_info['python_version'] != sys.version: - print(f"Warning: Python version mismatch") - print(f" Saved: {env_info['python_version']}") - print(f" Current: {sys.version}") - - # Load session - dill.load_session(filename) - - except FileNotFoundError: - print("Environment info not found, loading session anyway...") - dill.load_session(filename) -``` - -## Integration with Development Tools - -### Jupyter Notebook Integration - -```python -# In Jupyter notebook cells -import dill - -# Save notebook state -def save_notebook_session(): - """Save current notebook session.""" - dill.dump_session('notebook_session.pkl') - print("Notebook session saved") - -def load_notebook_session(): - """Load notebook session.""" - dill.load_session('notebook_session.pkl') - print("Notebook session loaded") - -# Magic command integration (requires IPython) -from IPython.core.magic import register_line_magic - -@register_line_magic -def save_dill(line): - """Magic command to save session.""" - filename = line.strip() or 'session.pkl' - dill.dump_session(filename) - print(f"Session saved to {filename}") - -@register_line_magic -def load_dill(line): - """Magic command to load session.""" - filename = line.strip() or 'session.pkl' - dill.load_session(filename) - print(f"Session loaded from {filename}") - -# Usage: %save_dill my_session.pkl -``` - -## Best Practices - -### Session Management Guidelines - -- **Regular Checkpoints**: Save session state before major experiments or changes -- **Descriptive Naming**: Use meaningful filenames that describe the session state -- **Environment Documentation**: Include metadata about the environment when sharing sessions -- **Selective Restoration**: Use `load_module_asdict` to inspect before full restoration -- **Clean-up**: Remove temporary variables before saving to reduce file size - -### Performance Considerations - -- **Large Sessions**: Break down large sessions into smaller, focused modules -- **Memory Usage**: Be aware that session files can be large for complex environments -- **Loading Time**: Session restoration can take time for environments with many dependencies -- **Cross-platform**: Test session files across different platforms if portability is needed \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/source-analysis.md b/.tessl/tiles/tessl/pypi-dill/docs/source-analysis.md deleted file mode 100644 index 1c9f3757c..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/source-analysis.md +++ /dev/null @@ -1,576 +0,0 @@ -# Source Code Analysis - -dill provides powerful tools for extracting, analyzing, and manipulating source code from Python objects, enabling introspection, code generation, and dynamic analysis capabilities. - -## Source Code Extraction - -### Primary Source Functions - -```python { .api } -def getsource(object, alias='', lstrip=False, enclosing=False, force=False, builtin=False): - """ - Get source code for an object. - - Extracts the source code of functions, classes, methods, and other - code objects, providing string representation of the original code. - The source code for interactively-defined objects are extracted from - the interpreter's history. - - Parameters: - - object: object to get source for (module, class, method, function, traceback, frame, or code object) - - alias: str, alias name for the object (adds line of code that renames the object) - - lstrip: bool, ensure there is no indentation in the first line of code - - enclosing: bool, include enclosing code and dependencies - - force: bool, catch (TypeError,IOError) and try to use import hooks - - builtin: bool, force an import for any builtins - - Returns: - str: source code as single string - - Raises: - - IOError: when source code cannot be retrieved - - TypeError: for objects where source code is unavailable (e.g. builtins) - """ - -def getsourcelines(object, lstrip=False, enclosing=False): - """ - Get source lines for an object. - - Returns the source code as a list of lines along with the starting - line number in the original file. - - Parameters: - - object: object to get source lines for - - lstrip: bool, left-strip whitespace from source lines - - enclosing: bool, include enclosing scope - - Returns: - tuple: (list of source lines, starting line number) - - Raises: - - OSError: when source cannot be found - """ - -def findsource(object): - """ - Find source code for an object. - - Locate the source code for an object by examining the file system, - module imports, and other available sources. - - Parameters: - - object: object to find source for - - Returns: - tuple: (list of all source lines, starting line number) - - Raises: - - OSError: when source cannot be located - """ -``` - -### Source Code Utilities - -```python { .api } -def getblocks(object, lstrip=False, enclosing=False, locate=False): - """ - Get code blocks for an object. - - Extracts logical code blocks (functions, classes, methods) associated - with an object, providing structured access to code components. - - Parameters: - - object: object to get code blocks for - - lstrip: bool, left-strip whitespace from blocks - - enclosing: bool, include enclosing blocks - - locate: bool, include location information - - Returns: - list: list of code blocks with metadata - """ - -def dumpsource(object, alias='', new=False, enclose=True): - """ - Dump source code with additional metadata. - - Creates a comprehensive source dump including dependencies, - imports, and context information needed for code reconstruction. - - Parameters: - - object: object to dump source for - - alias: str, alias name for the object - - new: bool, create new-style source dump format - - enclose: bool, enclose source with additional context - - Returns: - str: comprehensive source dump - """ -``` - -## Import Analysis - -### Import Statement Generation - -```python { .api } -def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): - """ - Get the likely import string for the given object. - - Generates the appropriate import statement needed to access an object, - including module path resolution and alias handling. - - Parameters: - - obj: object to inspect and generate import for - - alias: str, alias name to use (renames the object on import) - - verify: bool, test the import string before returning it - - builtin: bool, force an import for builtins where possible - - enclosing: bool, get the import for the outermost enclosing callable - - Returns: - str: import statement as string - """ - -def getimportable(obj, alias='', byname=True, explicit=False): - """ - Get importable representation of an object. - - Creates a representation that can be used to recreate the object - through import statements and attribute access. - - Parameters: - - obj: object to make importable - - alias: str, alias name to use - - byname: bool, prefer name-based imports over direct references - - explicit: bool, use explicit import paths - - Returns: - str: importable representation - """ - -def likely_import(obj, passive=False, explicit=False): - """ - Get likely import statement for an object. - - Attempts to determine the most likely import statement that would - provide access to the given object. - - Parameters: - - obj: object to analyze - - passive: bool, use passive analysis without side effects - - explicit: bool, prefer explicit import statements - - Returns: - str: likely import statement - """ -``` - -### Object Identification - -```python { .api } -def getname(obj, force=False, fqn=False): - """ - Get the name of an object. - - Attempts to determine the canonical name of an object by examining - its attributes, module context, and definition location. - - Parameters: - - obj: object to get name for - - force: bool, force name extraction even if not directly available - - fqn: bool, return fully qualified name including module path - - Returns: - str: object name or None if name cannot be determined - """ - -def isfrommain(obj): - """ - Check if object is from __main__ module. - - Determines whether an object originates from the main module, - which affects how it should be serialized and imported. - - Parameters: - - obj: object to check - - Returns: - bool: True if object is from __main__ module - """ - -def isdynamic(obj): - """ - Check if object is dynamically created. - - Determines whether an object was created dynamically at runtime - rather than being defined in source code. - - Parameters: - - obj: object to check - - Returns: - bool: True if object is dynamically created - """ -``` - -## Usage Examples - -### Basic Source Extraction - -```python -import dill.source as source - -# Define a function -def example_function(x, y=10): - """Example function with default parameter.""" - result = x + y - return result * 2 - -# Get source code -source_code = source.getsource(example_function) -print(source_code) -# Output: Full function definition as string - -# Get source lines with line numbers -lines, start_line = source.getsourcelines(example_function) -print(f"Function starts at line {start_line}") -for i, line in enumerate(lines): - print(f"{start_line + i}: {line.rstrip()}") -``` - -### Advanced Source Analysis - -```python -import dill.source as source - -class ExampleClass: - """Example class for source analysis.""" - - def __init__(self, value): - self.value = value - - def method(self, multiplier=2): - return self.value * multiplier - - @staticmethod - def static_method(x): - return x ** 2 - -# Analyze class source -class_source = source.getsource(ExampleClass) -print("Class source:") -print(class_source) - -# Get method source separately -method_source = source.getsource(ExampleClass.method) -print("\nMethod source:") -print(method_source) - -# Get source blocks -blocks = source.getblocks(ExampleClass) -print(f"\nFound {len(blocks)} code blocks in class") -``` - -### Import Generation - -```python -import dill.source as source -import json -from collections import defaultdict - -# Generate import statements for various objects -objects_to_import = [ - json.dumps, - defaultdict, - ExampleClass, - example_function -] - -for obj in objects_to_import: - try: - import_stmt = source.getimport(obj) - name = source.getname(obj) or str(obj) - print(f"{name}: {import_stmt}") - except Exception as e: - print(f"Could not generate import for {obj}: {e}") - -# Generate imports with aliases -alias_import = source.getimport(json.dumps, alias='json_serialize') -print(f"With alias: {alias_import}") -``` - -### Dynamic Code Analysis - -```python -import dill.source as source - -# Create dynamic function -exec(''' -def dynamic_function(x): - return x * 3 + 1 -''') - -# Analyze dynamic objects -if source.isdynamic(dynamic_function): - print("Function was created dynamically") - -if source.isfrommain(dynamic_function): - print("Function is from __main__ module") - -# Try to get source of dynamic function -try: - dynamic_source = source.getsource(dynamic_function) - print("Dynamic function source:") - print(dynamic_source) -except OSError: - print("Cannot get source for dynamic function") -``` - -### Source Code Modification - -```python -import dill.source as source - -def modify_function_source(func, modifications): - """Modify function source code.""" - try: - # Get original source - original_source = source.getsource(func) - - # Apply modifications - modified_source = original_source - for old, new in modifications.items(): - modified_source = modified_source.replace(old, new) - - # Create new function from modified source - namespace = {} - exec(modified_source, namespace) - - # Find the function in the namespace - func_name = source.getname(func) - if func_name in namespace: - return namespace[func_name] - - return None - - except Exception as e: - print(f"Failed to modify function: {e}") - return None - -# Example usage -def original_add(a, b): - return a + b - -# Modify to multiply instead -modifications = {'a + b': 'a * b'} -modified_func = modify_function_source(original_add, modifications) - -if modified_func: - print(f"Original: {original_add(3, 4)}") # 7 - print(f"Modified: {modified_func(3, 4)}") # 12 -``` - -## Advanced Features - -### Source Code Archival - -```python -import dill.source as source -import json - -def archive_source_code(objects, filename): - """Archive source code for multiple objects.""" - archive = {} - - for name, obj in objects.items(): - try: - obj_info = { - 'source': source.getsource(obj), - 'import': source.getimport(obj, verify=False), - 'name': source.getname(obj), - 'is_dynamic': source.isdynamic(obj), - 'is_from_main': source.isfrommain(obj) - } - archive[name] = obj_info - except Exception as e: - archive[name] = {'error': str(e)} - - with open(filename, 'w') as f: - json.dump(archive, f, indent=2) - - return archive - -# Archive important functions and classes -objects_to_archive = { - 'example_function': example_function, - 'ExampleClass': ExampleClass, - 'json_dumps': json.dumps -} - -archive = archive_source_code(objects_to_archive, 'source_archive.json') -print(f"Archived {len(archive)} objects") -``` - -### Code Dependency Analysis - -```python -import dill.source as source -import dill.detect as detect -import ast - -def analyze_dependencies(func): - """Analyze function dependencies.""" - try: - # Get source code - source_code = source.getsource(func) - - # Parse AST - tree = ast.parse(source_code) - - # Find imports and name references - dependencies = { - 'imports': [], - 'globals': [], - 'builtins': [] - } - - for node in ast.walk(tree): - if isinstance(node, ast.Import): - for alias in node.names: - dependencies['imports'].append(alias.name) - elif isinstance(node, ast.ImportFrom): - module = node.module or '' - for alias in node.names: - dependencies['imports'].append(f"{module}.{alias.name}") - - # Get global variables referenced - global_vars = detect.referredglobals(func, recurse=True) - dependencies['globals'] = list(global_vars.keys()) - - return dependencies - - except Exception as e: - return {'error': str(e)} - -# Analyze function dependencies -deps = analyze_dependencies(example_function) -print("Function dependencies:") -for dep_type, items in deps.items(): - if items: - print(f" {dep_type}: {items}") -``` - -### Interactive Source Browser - -```python -import dill.source as source - -class SourceBrowser: - """Interactive source code browser.""" - - def __init__(self): - self.history = [] - - def browse(self, obj): - """Browse source code for an object.""" - try: - obj_name = source.getname(obj) or str(obj) - self.history.append(obj_name) - - print(f"\n{'='*60}") - print(f"Source for {obj_name}") - print(f"{'='*60}") - - # Show basic info - print(f"Type: {type(obj).__name__}") - print(f"From main: {source.isfrommain(obj)}") - print(f"Dynamic: {source.isdynamic(obj)}") - - # Show import statement - try: - import_stmt = source.getimport(obj, verify=False) - print(f"Import: {import_stmt}") - except: - print("Import: Not available") - - print(f"\nSource code:") - print("-" * 40) - - # Show source - source_code = source.getsource(obj) - print(source_code) - - except Exception as e: - print(f"Error browsing {obj}: {e}") - - def show_history(self): - """Show browsing history.""" - print("\nBrowsing history:") - for i, item in enumerate(self.history): - print(f" {i+1}. {item}") - -# Usage -browser = SourceBrowser() -browser.browse(example_function) -browser.browse(ExampleClass) -browser.show_history() -``` - -## Integration with Serialization - -### Source-Aware Pickling - -```python -import dill -import dill.source as source - -def pickle_with_source(obj, filename): - """Pickle object along with its source code.""" - # Create enhanced object with source - enhanced_obj = { - 'object': obj, - 'metadata': { - 'source': None, - 'import': None, - 'name': source.getname(obj), - 'type': type(obj).__name__ - } - } - - # Try to get source and import info - try: - enhanced_obj['metadata']['source'] = source.getsource(obj) - except: - pass - - try: - enhanced_obj['metadata']['import'] = source.getimport(obj, verify=False) - except: - pass - - # Pickle enhanced object - with open(filename, 'wb') as f: - dill.dump(enhanced_obj, f) - -def unpickle_with_source(filename): - """Unpickle object and display source information.""" - with open(filename, 'rb') as f: - enhanced_obj = dill.load(f) - - obj = enhanced_obj['object'] - metadata = enhanced_obj['metadata'] - - print(f"Loaded {metadata['type']}: {metadata['name']}") - - if metadata['import']: - print(f"Import: {metadata['import']}") - - if metadata['source']: - print("Source code:") - print(metadata['source']) - - return obj - -# Usage -pickle_with_source(example_function, 'function_with_source.pkl') -restored_func = unpickle_with_source('function_with_source.pkl') -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/temp-operations.md b/.tessl/tiles/tessl/pypi-dill/docs/temp-operations.md deleted file mode 100644 index a4dccddef..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/temp-operations.md +++ /dev/null @@ -1,705 +0,0 @@ -# Temporary Operations - -dill.temp provides utilities for temporary file operations with serialization, stream capture, and IO buffer management for testing, development workflows, and temporary data handling. - -## File Operations - -### Temporary File Serialization - -```python { .api } -def dump(object, **kwds): - """ - Dump object to a NamedTemporaryFile using dill.dump. - - Serializes an object to a temporary file and returns the file handle, - useful for quick testing and temporary storage without managing filenames. - - Parameters: - - object: object to serialize to temporary file - - **kwds: optional keyword arguments including: - - suffix: str, file name suffix (default: no suffix) - - prefix: str, file name prefix (default: system default) - - other arguments passed to NamedTemporaryFile and dill.dump - - Returns: - file handle: NamedTemporaryFile handle containing serialized object - - Raises: - - PicklingError: when object cannot be serialized - - IOError: when temporary file operations fail - """ - -def load(file, **kwds): - """ - Load an object that was stored with dill.temp.dump. - - Deserializes an object from a file, typically used with temporary - files created by temp.dump but works with any dill-compatible file. - - Parameters: - - file: file handle or str, file handle or path to file containing serialized object - - **kwds: optional keyword arguments including: - - mode: str, file open mode ('r' or 'rb', default: 'rb') - - other arguments passed to open() and dill.load - - Returns: - object: deserialized object - - Raises: - - UnpicklingError: when object cannot be deserialized - - IOError: when file operations fail - """ -``` - -### Source Code File Operations - -```python { .api } -def load_source(file, **kwds): - """ - Load source code from file. - - Reads and executes Python source code from a file, returning - the executed namespace or specific objects. - - Parameters: - - file: str, path to Python source file - - **kwds: keyword arguments for source execution control - - Returns: - object: result of source code execution - - Raises: - - SyntaxError: when source code is invalid - - IOError: when file cannot be read - """ - -def dump_source(object, **kwds): - """ - Dump source code to temporary file. - - Extracts source code from an object and writes it to a temporary - file, useful for code analysis and temporary source storage. - - Parameters: - - object: object to extract source code from - - **kwds: keyword arguments for source extraction - - Returns: - str: path to temporary file containing source code - - Raises: - - OSError: when source code cannot be extracted - - IOError: when file operations fail - """ -``` - -## IO Buffer Operations - -### Buffer-based Serialization - -```python { .api } -def loadIO(buffer, **kwds): - """ - Load object from IO buffer. - - Deserializes an object from a bytes buffer or file-like object, - providing memory-based deserialization for testing and processing. - - Parameters: - - buffer: bytes or file-like object containing serialized data - - **kwds: keyword arguments passed to dill.load - - Returns: - object: deserialized object - - Raises: - - UnpicklingError: when deserialization fails - - TypeError: when buffer type is not supported - """ - -def dumpIO(object, **kwds): - """ - Dump object to IO buffer. - - Serializes an object to a bytes buffer, returning the buffer - for further processing or storage. - - Parameters: - - object: object to serialize - - **kwds: keyword arguments passed to dill.dumps - - Returns: - bytes: serialized object as bytes buffer - - Raises: - - PicklingError: when serialization fails - """ - -def loadIO_source(buffer, **kwds): - """ - Load source code from IO buffer. - - Reads and executes Python source code from a buffer, - useful for dynamic code execution and testing. - - Parameters: - - buffer: str or bytes containing Python source code - - **kwds: keyword arguments for execution control - - Returns: - object: result of source code execution - - Raises: - - SyntaxError: when source code is invalid - """ - -def dumpIO_source(object, **kwds): - """ - Dump source code to IO buffer. - - Extracts source code from an object and returns it as a string buffer, - useful for in-memory source code processing. - - Parameters: - - object: object to extract source code from - - **kwds: keyword arguments for source extraction - - Returns: - str: source code as string buffer - - Raises: - - OSError: when source code cannot be extracted - """ -``` - -## Stream Capture - -### Output Capture Utilities - -```python { .api } -def capture(stream='stdout'): - """ - Capture stdout or stderr stream. - - Context manager that captures output from stdout or stderr, - useful for testing, logging, and output analysis. - - Parameters: - - stream: str, stream name ('stdout' or 'stderr') - - Returns: - context manager that captures stream output - - Usage: - with dill.temp.capture('stdout') as output: - print("Hello") - captured_text = output.getvalue() - """ -``` - -## Usage Examples - -### Basic Temporary File Operations - -```python -import dill.temp as temp - -# Create some test data -def test_function(x, y): - return x * y + 42 - -class TestClass: - def __init__(self, value): - self.value = value - - def get_doubled(self): - return self.value * 2 - -test_data = { - 'function': test_function, - 'class': TestClass, - 'instance': TestClass(10), - 'list': [1, 2, 3, 4, 5] -} - -# Dump to temporary file -temp_file = temp.dump(test_data) -print(f"Data saved to temporary file: {temp_file}") - -# Load from temporary file -restored_data = temp.load(temp_file) -print(f"Restored data keys: {list(restored_data.keys())}") - -# Test restored functionality -restored_function = restored_data['function'] -result = restored_function(5, 3) -print(f"Function result: {result}") # Should be 57 - -restored_instance = restored_data['instance'] -doubled = restored_instance.get_doubled() -print(f"Instance method result: {doubled}") # Should be 20 -``` - -### Source Code Operations - -```python -import dill.temp as temp - -# Define a function to work with -def example_algorithm(data, threshold=0.5): - """Example algorithm for demonstration.""" - processed = [] - for item in data: - if isinstance(item, (int, float)) and item > threshold: - processed.append(item * 2) - else: - processed.append(item) - return processed - -# Dump source code to temporary file -source_file = temp.dump_source(example_algorithm) -print(f"Source code saved to: {source_file}") - -# Load and examine source code -with open(source_file, 'r') as f: - source_content = f.read() - print("Source code:") - print(source_content) - -# Load source code as executable -loaded_func = temp.load_source(source_file) -print(f"Loaded function: {loaded_func}") - -# Test loaded function -test_data = [0.2, 0.8, 1.5, 0.1, 2.0] -result = loaded_func(test_data, threshold=0.6) -print(f"Algorithm result: {result}") -``` - -### IO Buffer Operations - -```python -import dill.temp as temp -import io - -# Complex object for testing -complex_data = { - 'functions': [lambda x: x**2, lambda x: x**3], - 'nested': {'level1': {'level2': [1, 2, 3]}}, - 'instance': TestClass(42) -} - -# Serialize to buffer -buffer_data = temp.dumpIO(complex_data) -print(f"Serialized to buffer: {len(buffer_data)} bytes") - -# Deserialize from buffer -restored_from_buffer = temp.loadIO(buffer_data) -print("Restored from buffer successfully") - -# Test functionality -square_func = restored_from_buffer['functions'][0] -print(f"Square function: {square_func(5)}") # Should be 25 - -# Source code buffer operations -source_buffer = temp.dumpIO_source(example_algorithm) -print(f"Source code buffer length: {len(source_buffer)} characters") - -# Load source from buffer -namespace = {} -exec(source_buffer, namespace) -func_from_buffer = namespace['example_algorithm'] -result = func_from_buffer([1, 2, 3], threshold=1.5) -print(f"Function from source buffer: {result}") -``` - -### Stream Capture - -```python -import dill.temp as temp - -def noisy_function(): - """Function that produces output.""" - print("Starting processing...") - print("Processing item 1") - print("Processing item 2") - print("Processing complete!") - return "Result" - -def error_function(): - """Function that produces error output.""" - import sys - print("This goes to stdout") - print("This goes to stderr", file=sys.stderr) - return "Done" - -# Capture stdout -with temp.capture('stdout') as stdout_capture: - result = noisy_function() - -stdout_output = stdout_capture.getvalue() -print(f"Captured stdout ({len(stdout_output)} chars):") -print(repr(stdout_output)) - -# Capture stderr -with temp.capture('stderr') as stderr_capture: - result = error_function() - -stderr_output = stderr_capture.getvalue() -print(f"Captured stderr: {repr(stderr_output)}") - -# Capture both streams -import sys -from contextlib import redirect_stdout, redirect_stderr - -def capture_both_streams(func): - """Capture both stdout and stderr.""" - stdout_buffer = io.StringIO() - stderr_buffer = io.StringIO() - - with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer): - result = func() - - return { - 'result': result, - 'stdout': stdout_buffer.getvalue(), - 'stderr': stderr_buffer.getvalue() - } - -# Usage -captured = capture_both_streams(error_function) -print(f"Function result: {captured['result']}") -print(f"Stdout: {repr(captured['stdout'])}") -print(f"Stderr: {repr(captured['stderr'])}") -``` - -## Advanced Use Cases - -### Testing Framework Integration - -```python -import dill.temp as temp -import unittest -import os - -class TempFileTestCase(unittest.TestCase): - """Test case with temporary file management.""" - - def setUp(self): - self.temp_files = [] - - def tearDown(self): - # Clean up temporary files - for temp_file in self.temp_files: - try: - if os.path.exists(temp_file): - os.remove(temp_file) - except: - pass - - def create_temp_object(self, obj): - """Create temporary file for object and track it.""" - temp_file = temp.dump(obj) - self.temp_files.append(temp_file) - return temp_file - - def test_function_serialization(self): - """Test function serialization via temporary files.""" - def test_func(x): - return x * 2 - - # Serialize to temp file - temp_file = self.create_temp_object(test_func) - self.assertTrue(os.path.exists(temp_file)) - - # Load and test - loaded_func = temp.load(temp_file) - self.assertEqual(loaded_func(5), 10) - - def test_complex_object_roundtrip(self): - """Test complex object serialization roundtrip.""" - complex_obj = { - 'data': [1, 2, 3, 4, 5], - 'func': lambda x: sum(x), - 'nested': {'key': 'value'} - } - - temp_file = self.create_temp_object(complex_obj) - loaded_obj = temp.load(temp_file) - - # Test loaded functionality - self.assertEqual(loaded_obj['data'], [1, 2, 3, 4, 5]) - self.assertEqual(loaded_obj['func']([1, 2, 3]), 6) - self.assertEqual(loaded_obj['nested']['key'], 'value') - -# Run tests -if __name__ == '__main__': - unittest.main() -``` - -### Development Workflow Tools - -```python -import dill.temp as temp -import datetime -import json - -class DevelopmentSession: - """Manage development session with temporary serialization.""" - - def __init__(self, session_name): - self.session_name = session_name - self.snapshots = [] - self.current_objects = {} - - def add_object(self, name, obj): - """Add object to current session.""" - self.current_objects[name] = obj - print(f"Added {name} to session") - - def take_snapshot(self, description=""): - """Take snapshot of current session state.""" - timestamp = datetime.datetime.now().isoformat() - - # Serialize current objects to temporary file - temp_file = temp.dump(self.current_objects) - - snapshot = { - 'timestamp': timestamp, - 'description': description, - 'temp_file': temp_file, - 'object_count': len(self.current_objects) - } - - self.snapshots.append(snapshot) - print(f"Snapshot taken: {len(self.snapshots)} total snapshots") - return len(self.snapshots) - 1 - - def restore_snapshot(self, snapshot_index): - """Restore session from snapshot.""" - if 0 <= snapshot_index < len(self.snapshots): - snapshot = self.snapshots[snapshot_index] - - # Load objects from temporary file - self.current_objects = temp.load(snapshot['temp_file']) - - print(f"Restored snapshot from {snapshot['timestamp']}") - print(f"Objects restored: {list(self.current_objects.keys())}") - else: - print(f"Invalid snapshot index: {snapshot_index}") - - def list_snapshots(self): - """List all snapshots.""" - for i, snapshot in enumerate(self.snapshots): - print(f"{i}: {snapshot['timestamp']} - {snapshot['description']} ({snapshot['object_count']} objects)") - - def get_object(self, name): - """Get object from current session.""" - return self.current_objects.get(name) - - def export_session_info(self, filename): - """Export session metadata to JSON.""" - session_info = { - 'session_name': self.session_name, - 'snapshots': [ - { - 'timestamp': s['timestamp'], - 'description': s['description'], - 'object_count': s['object_count'] - } - for s in self.snapshots - ], - 'current_objects': list(self.current_objects.keys()) - } - - with open(filename, 'w') as f: - json.dump(session_info, f, indent=2) - - print(f"Session info exported to {filename}") - -# Usage example -session = DevelopmentSession("algorithm_development") - -# Add some objects to work with -def fibonacci(n): - if n <= 1: - return n - return fibonacci(n-1) + fibonacci(n-2) - -class DataProcessor: - def __init__(self, multiplier=1): - self.multiplier = multiplier - - def process(self, data): - return [x * self.multiplier for x in data] - -session.add_object('fib_func', fibonacci) -session.add_object('processor', DataProcessor(2)) -session.add_object('test_data', [1, 2, 3, 4, 5]) - -# Take initial snapshot -session.take_snapshot("Initial development state") - -# Modify objects -session.add_object('processor', DataProcessor(3)) # Change multiplier -session.add_object('results', session.get_object('processor').process(session.get_object('test_data'))) - -# Take another snapshot -session.take_snapshot("After modifications") - -# Show snapshots -session.list_snapshots() - -# Restore previous state -session.restore_snapshot(0) - -# Export session info -session.export_session_info('dev_session.json') -``` - -### Performance Benchmarking - -```python -import dill.temp as temp -import time -import sys - -class SerializationBenchmark: - """Benchmark serialization performance with temporary operations.""" - - def __init__(self): - self.results = {} - - def benchmark_temp_operations(self, test_objects, iterations=10): - """Benchmark temporary file operations.""" - for name, obj in test_objects.items(): - print(f"Benchmarking {name}...") - - # Measure temp.dump performance - dump_times = [] - load_times = [] - file_sizes = [] - - for i in range(iterations): - # Time dump operation - start_time = time.time() - temp_file = temp.dump(obj) - dump_time = time.time() - start_time - dump_times.append(dump_time) - - # Get file size - file_size = os.path.getsize(temp_file) - file_sizes.append(file_size) - - # Time load operation - start_time = time.time() - temp.load(temp_file) - load_time = time.time() - start_time - load_times.append(load_time) - - # Clean up - os.remove(temp_file) - - self.results[name] = { - 'avg_dump_time': sum(dump_times) / len(dump_times), - 'avg_load_time': sum(load_times) / len(load_times), - 'avg_file_size': sum(file_sizes) / len(file_sizes), - 'min_dump_time': min(dump_times), - 'max_dump_time': max(dump_times), - 'min_load_time': min(load_times), - 'max_load_time': max(load_times) - } - - def benchmark_io_operations(self, test_objects, iterations=10): - """Benchmark IO buffer operations.""" - for name, obj in test_objects.items(): - print(f"Benchmarking IO operations for {name}...") - - dump_times = [] - load_times = [] - buffer_sizes = [] - - for i in range(iterations): - # Time dumpIO operation - start_time = time.time() - buffer_data = temp.dumpIO(obj) - dump_time = time.time() - start_time - dump_times.append(dump_time) - - buffer_sizes.append(len(buffer_data)) - - # Time loadIO operation - start_time = time.time() - temp.loadIO(buffer_data) - load_time = time.time() - start_time - load_times.append(load_time) - - io_results = { - 'avg_dump_time': sum(dump_times) / len(dump_times), - 'avg_load_time': sum(load_times) / len(load_times), - 'avg_buffer_size': sum(buffer_sizes) / len(buffer_sizes) - } - - if name in self.results: - self.results[name]['io'] = io_results - else: - self.results[name] = {'io': io_results} - - def print_results(self): - """Print benchmark results.""" - print("\\nSerialization Benchmark Results") - print("=" * 60) - - for name, results in self.results.items(): - print(f"\\n{name}:") - print("-" * 30) - - if 'avg_dump_time' in results: # File operations - print(f"File Operations:") - print(f" Avg dump time: {results['avg_dump_time']:.4f}s") - print(f" Avg load time: {results['avg_load_time']:.4f}s") - print(f" Avg file size: {results['avg_file_size']:,.0f} bytes") - - if 'io' in results: # IO operations - io = results['io'] - print(f"IO Buffer Operations:") - print(f" Avg dump time: {io['avg_dump_time']:.4f}s") - print(f" Avg load time: {io['avg_load_time']:.4f}s") - print(f" Avg buffer size: {io['avg_buffer_size']:,.0f} bytes") - -# Run benchmark -benchmark = SerializationBenchmark() - -# Create test objects of varying complexity -test_objects = { - 'simple_list': list(range(1000)), - 'function': lambda x: x**2 + x + 1, - 'class_instance': TestClass(100), - 'nested_dict': {'level1': {'level2': {'level3': list(range(100))}}}, - 'mixed_structure': { - 'data': list(range(500)), - 'funcs': [lambda x: x+1, lambda x: x*2], - 'objects': [TestClass(i) for i in range(10)] - } -} - -benchmark.benchmark_temp_operations(test_objects, iterations=5) -benchmark.benchmark_io_operations(test_objects, iterations=5) -benchmark.print_results() -``` - -## Best Practices - -### Temporary File Management - -1. **Cleanup**: Always clean up temporary files when done -2. **Path Management**: Store temp file paths for later cleanup -3. **Error Handling**: Handle file operation errors gracefully -4. **Security**: Be aware of temporary file permissions and location - -### Performance Optimization - -1. **Buffer vs File**: Use IO buffers for small objects, files for large ones -2. **Memory Usage**: Monitor memory usage with large temporary operations -3. **Disk Space**: Monitor disk space usage with temporary files -4. **Cleanup Frequency**: Clean up temporary files regularly in long-running processes \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/docs/type-registry.md b/.tessl/tiles/tessl/pypi-dill/docs/type-registry.md deleted file mode 100644 index 4e12640a1..000000000 --- a/.tessl/tiles/tessl/pypi-dill/docs/type-registry.md +++ /dev/null @@ -1,397 +0,0 @@ -# Type Registry System - -dill provides a flexible type registry system that allows registration of custom types and extension of serialization capabilities to handle new object types that are not supported by default. - -## Core Registry Functions - -### Type Registration - -```python { .api } -def register(t): - """ - Register a type with the pickler. - - Decorator function that registers a custom pickling function for a specific - type, allowing dill to handle objects of that type during serialization. - - Parameters: - - t: type, the type to register for custom pickling - - Returns: - function: decorator function that takes the pickling function - - Usage: - @dill.register(MyCustomType) - def save_custom_type(pickler, obj): - # Custom pickling logic - pass - """ - -def pickle(t, func): - """ - Add a type to the pickle dispatch table. - - Directly associates a type with a pickling function in the dispatch table, - enabling automatic handling of objects of that type. - - Parameters: - - t: type, the type to add to dispatch table - - func: function, pickling function that handles objects of type t - - Returns: - None - - Raises: - - TypeError: when type or function parameters are invalid - """ - -def extend(use_dill=True): - """ - Add or remove dill types to/from the pickle registry. - - Controls whether dill's extended types are available in the standard - pickle module's dispatch table, allowing integration with existing code. - - Parameters: - - use_dill: bool, if True extend dispatch table with dill types, - if False revert to standard pickle types only - - Returns: - None - """ -``` - -### Registry Inspection - -```python { .api } -def dispatch_table(): - """ - Get the current dispatch table. - - Returns the current pickle dispatch table showing all registered - types and their associated pickling functions. - - Returns: - dict: mapping of types to pickling functions - """ -``` - -## Usage Examples - -### Basic Type Registration - -```python -import dill - -# Define a custom class -class CustomData: - def __init__(self, data, metadata=None): - self.data = data - self.metadata = metadata or {} - - def __repr__(self): - return f"CustomData({self.data}, {self.metadata})" - -# Register custom pickling function using decorator -@dill.register(CustomData) -def save_custom_data(pickler, obj): - """Custom pickling function for CustomData objects.""" - # Save the class reference - pickler.save_reduce(_restore_custom_data, - (obj.data, obj.metadata), obj=obj) - -def _restore_custom_data(data, metadata): - """Helper function to restore CustomData objects.""" - return CustomData(data, metadata) - -# Test the registration -custom_obj = CustomData([1, 2, 3], {"version": "1.0"}) - -# Now it can be pickled -serialized = dill.dumps(custom_obj) -restored = dill.loads(serialized) - -print(f"Original: {custom_obj}") -print(f"Restored: {restored}") -``` - -### Direct Dispatch Table Modification - -```python -import dill - -class SpecialContainer: - def __init__(self, items): - self.items = list(items) - - def __eq__(self, other): - return isinstance(other, SpecialContainer) and self.items == other.items - -def pickle_special_container(pickler, obj): - """Pickling function for SpecialContainer.""" - # Use pickle protocol to save class and items - pickler.save_reduce(SpecialContainer, (obj.items,), obj=obj) - -# Add to dispatch table directly -dill.pickle(SpecialContainer, pickle_special_container) - -# Test the registration -container = SpecialContainer([1, 2, 3, 4]) -pickled_data = dill.dumps(container) -unpickled_container = dill.loads(pickled_data) - -print(f"Original == Restored: {container == unpickled_container}") -``` - -### Complex Type Registration - -```python -import dill -import weakref - -class Node: - """Tree node with parent/child relationships.""" - def __init__(self, value, parent=None): - self.value = value - self.children = [] - self._parent_ref = None - if parent: - self.set_parent(parent) - - def set_parent(self, parent): - if self._parent_ref: - old_parent = self._parent_ref() - if old_parent: - old_parent.children.remove(self) - - self._parent_ref = weakref.ref(parent) if parent else None - if parent: - parent.children.append(self) - - @property - def parent(self): - return self._parent_ref() if self._parent_ref else None - -# Register complex type with custom handling -@dill.register(Node) -def save_node(pickler, obj): - """Custom pickling for Node objects.""" - # Save value and children (parent will be reconstructed) - pickler.save_reduce(_restore_node, - (obj.value, obj.children), obj=obj) - -def _restore_node(value, children): - """Restore Node with proper parent/child relationships.""" - node = Node(value) - - # Restore children and set parent relationships - for child in children: - if isinstance(child, Node): - child.set_parent(node) - else: - # Handle case where child was also pickled/unpickled - node.children.append(child) - - return node - -# Test complex type registration -root = Node("root") -child1 = Node("child1", root) -child2 = Node("child2", root) -grandchild = Node("grandchild", child1) - -# Pickle the entire tree -tree_data = dill.dumps(root) -restored_root = dill.loads(tree_data) - -print(f"Root children: {[child.value for child in restored_root.children]}") -print(f"Child1 parent: {restored_root.children[0].parent.value}") -``` - -### Registry Management - -```python -import dill - -class RegistryManager: - """Manage custom type registrations.""" - - def __init__(self): - self.registered_types = {} - self.original_dispatch = None - - def register_type(self, type_class, save_func, restore_func=None): - """Register a type with save and optional restore functions.""" - if restore_func: - # Store restore function for later use - self.registered_types[type_class] = { - 'save': save_func, - 'restore': restore_func - } - - # Create wrapper that includes restore function - def save_wrapper(pickler, obj): - pickler.save_reduce(restore_func, save_func(obj), obj=obj) - - dill.register(type_class)(save_wrapper) - else: - # Direct registration - dill.register(type_class)(save_func) - self.registered_types[type_class] = {'save': save_func} - - def unregister_type(self, type_class): - """Remove type from registry.""" - if type_class in self.registered_types: - # Remove from dill's dispatch table - dispatch = dill.dispatch_table() - if type_class in dispatch: - del dispatch[type_class] - - del self.registered_types[type_class] - - def list_registered_types(self): - """List all registered custom types.""" - return list(self.registered_types.keys()) - - def backup_dispatch_table(self): - """Backup current dispatch table.""" - self.original_dispatch = dill.dispatch_table().copy() - - def restore_dispatch_table(self): - \"\"\"Restore original dispatch table.\"\"\" - if self.original_dispatch: - current_dispatch = dill.dispatch_table() - current_dispatch.clear() - current_dispatch.update(self.original_dispatch) - -# Usage example -registry = RegistryManager() - -# Backup original state -registry.backup_dispatch_table() - -# Register multiple types -registry.register_type( - CustomData, - lambda obj: (obj.data, obj.metadata), - lambda data, meta: CustomData(data, meta) -) - -registry.register_type( - SpecialContainer, - lambda obj: (obj.items,), - lambda items: SpecialContainer(items) -) - -print(f"Registered types: {[t.__name__ for t in registry.list_registered_types()]}") - -# Test registrations -test_objects = [ - CustomData([1, 2, 3], {"test": True}), - SpecialContainer(["a", "b", "c"]) -] - -for obj in test_objects: - try: - data = dill.dumps(obj) - restored = dill.loads(data) - print(f"✓ {type(obj).__name__}: Successfully pickled and restored") - except Exception as e: - print(f"✗ {type(obj).__name__}: Failed - {e}") - -# Clean up -registry.restore_dispatch_table() -``` - -## Advanced Registry Techniques - -### Conditional Type Registration - -```python -import dill -import sys - -class ConditionalRegistry: - """Register types based on conditions.""" - - @staticmethod - def register_if_available(type_name, module_name, save_func, restore_func=None): - """Register type only if module is available.""" - try: - module = __import__(module_name) - type_class = getattr(module, type_name) - - if restore_func: - def save_wrapper(pickler, obj): - pickler.save_reduce(restore_func, save_func(obj), obj=obj) - dill.register(type_class)(save_wrapper) - else: - dill.register(type_class)(save_func) - - print(f"✓ Registered {type_name} from {module_name}") - return True - - except (ImportError, AttributeError) as e: - print(f"✗ Could not register {type_name}: {e}") - return False - - @staticmethod - def register_for_python_version(min_version, type_class, save_func): - """Register type only for specific Python versions.""" - if sys.version_info >= min_version: - dill.register(type_class)(save_func) - print(f"✓ Registered {type_class.__name__} for Python {sys.version_info[:2]}") - return True - else: - print(f"✗ Skipped {type_class.__name__} - requires Python {min_version}") - return False - -# Example usage -registry = ConditionalRegistry() - -# Register numpy array if numpy is available -registry.register_if_available( - 'ndarray', 'numpy', - lambda obj: (obj.tobytes(), obj.dtype, obj.shape), - lambda data, dtype, shape: __import__('numpy').frombuffer(data, dtype).reshape(shape) -) - -# Register pathlib.Path for Python 3.4+ -import pathlib -registry.register_for_python_version( - (3, 4), - pathlib.Path, - lambda obj: str(obj) -) -``` - -### Dynamic Type Discovery - -```python -import dill -import inspect - -class AutoRegistry: - """Automatically discover and register types.""" - - def __init__(self): - self.auto_registered = set() - - def auto_register_module(self, module, save_pattern=None, exclude=None): - """Automatically register types from a module.""" - exclude = exclude or [] - registered_count = 0 - - for name, obj in inspect.getmembers(module): - # Skip private and excluded items - if name.startswith('_') or name in exclude: - continue - - # Only register classes - if inspect.isclass(obj): - # Check if we can create a default save function - if self._can_auto_register(obj): - self._auto_register_type(obj) - registered_count += 1 - - print(f\"Auto-registered {registered_count} types from {module.__name__}\")\n return registered_count\n \n def _can_auto_register(self, type_class):\n \"\"\"Check if type can be auto-registered.\"\"\"\n try:\n # Check if type has __dict__ or __slots__\n if hasattr(type_class, '__dict__') or hasattr(type_class, '__slots__'):\n return True\n \n # Check if it's a simple dataclass-like structure\n if hasattr(type_class, '__init__'):\n sig = inspect.signature(type_class.__init__)\n # Simple heuristic: if all parameters have defaults or annotations\n return len(sig.parameters) <= 5 # Reasonable limit\n \n except Exception:\n pass\n \n return False\n \n def _auto_register_type(self, type_class):\n \"\"\"Automatically register a type with default behavior.\"\"\"\n if type_class in self.auto_registered:\n return\n \n def auto_save_func(pickler, obj):\n # Generic save function using __dict__ or __getstate__\n if hasattr(obj, '__getstate__'):\n state = obj.__getstate__()\n pickler.save_reduce(_auto_restore_with_state, \n (type_class, state), obj=obj)\n elif hasattr(obj, '__dict__'):\n pickler.save_reduce(_auto_restore_with_dict,\n (type_class, obj.__dict__), obj=obj)\n else:\n # Fallback to trying __reduce__\n pickler.save_reduce(type_class, (), obj=obj)\n \n dill.register(type_class)(auto_save_func)\n self.auto_registered.add(type_class)\n print(f\"Auto-registered {type_class.__name__}\")\n\ndef _auto_restore_with_state(type_class, state):\n \"\"\"Restore object using __setstate__.\"\"\"\n obj = type_class.__new__(type_class)\n if hasattr(obj, '__setstate__'):\n obj.__setstate__(state)\n else:\n obj.__dict__.update(state)\n return obj\n\ndef _auto_restore_with_dict(type_class, obj_dict):\n \"\"\"Restore object using __dict__.\"\"\"\n obj = type_class.__new__(type_class)\n obj.__dict__.update(obj_dict)\n return obj\n\n# Example usage\nauto_registry = AutoRegistry()\n\n# Auto-register types from a custom module\n# auto_registry.auto_register_module(my_custom_module, exclude=['BaseClass'])\n```\n\n## Integration with Existing Code\n\n### Backward Compatibility\n\n```python\nimport dill\nimport pickle\n\nclass CompatibilityManager:\n \"\"\"Manage compatibility between dill and standard pickle.\"\"\"\n \n def __init__(self):\n self.dill_enabled = True\n \n def enable_dill_types(self):\n \"\"\"Enable dill types in pickle dispatch table.\"\"\"\n dill.extend(use_dill=True)\n self.dill_enabled = True\n print(\"Dill types enabled in pickle\")\n \n def disable_dill_types(self):\n \"\"\"Disable dill types, use only standard pickle.\"\"\"\n dill.extend(use_dill=False)\n self.dill_enabled = False\n print(\"Dill types disabled, using standard pickle only\")\n \n def test_compatibility(self, test_objects):\n \"\"\"Test objects with both pickle and dill.\"\"\"\n results = {}\n \n for name, obj in test_objects.items():\n results[name] = {\n 'dill': self._test_with_dill(obj),\n 'pickle': self._test_with_pickle(obj)\n }\n \n return results\n \n def _test_with_dill(self, obj):\n try:\n data = dill.dumps(obj)\n dill.loads(data)\n return \"✓ Success\"\n except Exception as e:\n return f\"✗ Failed: {e}\"\n \n def _test_with_pickle(self, obj):\n try:\n data = pickle.dumps(obj)\n pickle.loads(data)\n return \"✓ Success\"\n except Exception as e:\n return f\"✗ Failed: {e}\"\n\n# Usage\ncompat = CompatibilityManager()\n\n# Test various objects\ntest_objects = {\n 'function': lambda x: x + 1,\n 'class': CustomData,\n 'instance': CustomData([1, 2, 3]),\n 'list': [1, 2, 3, 4, 5]\n}\n\nprint(\"Testing with dill types enabled:\")\ncompat.enable_dill_types()\nresults_enabled = compat.test_compatibility(test_objects)\n\nprint(\"\\nTesting with dill types disabled:\")\ncompat.disable_dill_types()\nresults_disabled = compat.test_compatibility(test_objects)\n\n# Compare results\nprint(\"\\nCompatibility Results:\")\nprint(\"-\" * 50)\nfor name in test_objects:\n print(f\"{name}:\")\n print(f\" Dill enabled - pickle: {results_enabled[name]['pickle']}\")\n print(f\" Dill disabled - pickle: {results_disabled[name]['pickle']}\")\n print(f\" Dill enabled - dill: {results_enabled[name]['dill']}\")\n\n# Re-enable for normal operation\ncompat.enable_dill_types()\n```\n\n## Best Practices\n\n### Registry Guidelines\n\n1. **Performance**: Keep pickling functions simple and fast\n2. **Robustness**: Handle edge cases and provide error recovery\n3. **Compatibility**: Test with different Python versions and environments\n4. **Documentation**: Document custom types and their pickling behavior\n5. **Testing**: Verify that pickled objects restore correctly with full functionality\n\n### Error Handling\n\n```python\nimport dill\nimport logging\n\nclass RobustRegistry:\n \"\"\"Registry with comprehensive error handling.\"\"\"\n \n def __init__(self):\n self.logger = logging.getLogger(__name__)\n \n def safe_register(self, type_class, save_func, restore_func=None):\n \"\"\"Register type with error handling.\"\"\"\n try:\n # Validate inputs\n if not isinstance(type_class, type):\n raise TypeError(\"type_class must be a type\")\n \n if not callable(save_func):\n raise TypeError(\"save_func must be callable\")\n \n if restore_func and not callable(restore_func):\n raise TypeError(\"restore_func must be callable\")\n \n # Test the registration with a dummy object\n self._test_registration(type_class, save_func, restore_func)\n \n # Perform actual registration\n if restore_func:\n def wrapper(pickler, obj):\n try:\n args = save_func(obj)\n pickler.save_reduce(restore_func, args, obj=obj)\n except Exception as e:\n self.logger.error(f\"Error saving {type_class.__name__}: {e}\")\n raise\n \n dill.register(type_class)(wrapper)\n else:\n dill.register(type_class)(save_func)\n \n self.logger.info(f\"Successfully registered {type_class.__name__}\")\n return True\n \n except Exception as e:\n self.logger.error(f\"Failed to register {type_class.__name__}: {e}\")\n return False\n \n def _test_registration(self, type_class, save_func, restore_func):\n \"\"\"Test registration with dummy data.\"\"\"\n # This would need to be implemented based on specific type requirements\n pass\n\n# Usage\nrobust_registry = RobustRegistry()\nlogging.basicConfig(level=logging.INFO)\n\nsuccess = robust_registry.safe_register(\n CustomData,\n lambda obj: (obj.data, obj.metadata),\n lambda data, meta: CustomData(data, meta)\n)\n\nif success:\n print(\"Registration successful\")\nelse:\n print(\"Registration failed - check logs\")\n``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-dill/tile.json b/.tessl/tiles/tessl/pypi-dill/tile.json deleted file mode 100644 index f3c94b616..000000000 --- a/.tessl/tiles/tessl/pypi-dill/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-dill", - "version": "0.4.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/dill@0.4.0", - "summary": "Extended Python serialization library that can serialize functions, classes, and complex objects that standard pickle cannot handle." -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/batches.md b/.tessl/tiles/tessl/pypi-google-genai/docs/batches.md deleted file mode 100644 index 1c9fc206f..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/batches.md +++ /dev/null @@ -1,408 +0,0 @@ -# Batch Processing - -Submit batch prediction jobs for high-volume inference with cost savings. Batch processing allows you to process large numbers of requests asynchronously at reduced costs compared to online inference, ideal for processing datasets, bulk content generation, and embeddings. - -## Capabilities - -### Create Batch Job - -Create a batch prediction job for content generation. Processes requests from Cloud Storage or BigQuery and writes results back. - -```python { .api } -class Batches: - """Synchronous batch prediction jobs API.""" - - def create( - self, - *, - model: str, - src: Union[str, list[dict]], - dest: Optional[str] = None, - config: Optional[CreateBatchJobConfig] = None - ) -> BatchJob: - """ - Create batch prediction job for content generation. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash'). - src (Union[str, list[dict]]): Source of requests. Can be: - - str: GCS URI ('gs://bucket/input.jsonl') or BigQuery table - ('bq://project.dataset.table') - - list[dict]: List of request dictionaries for inline requests - dest (str, optional): Destination for results. GCS URI ('gs://bucket/output') - or BigQuery table ('bq://project.dataset.table'). Required for GCS/BigQuery - sources, optional for inline requests. - config (CreateBatchJobConfig, optional): Job configuration. - - Returns: - BatchJob: Created batch job with name and status. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncBatches: - """Asynchronous batch prediction jobs API.""" - - async def create( - self, - *, - model: str, - src: Union[str, list[dict]], - dest: Optional[str] = None, - config: Optional[CreateBatchJobConfig] = None - ) -> BatchJob: - """Async version of create.""" - ... -``` - -**Usage Example - GCS:** - -```python -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Create batch job from GCS -job = client.batches.create( - model='gemini-2.0-flash', - src='gs://my-bucket/requests.jsonl', - dest='gs://my-bucket/results/' -) - -print(f"Job created: {job.name}") -print(f"State: {job.state}") - -# Poll for completion -import time -while job.state in ['JOB_STATE_PENDING', 'JOB_STATE_RUNNING']: - time.sleep(60) - job = client.batches.get(name=job.name) - print(f"State: {job.state}") - -print(f"Final state: {job.state}") -if job.state == 'JOB_STATE_SUCCEEDED': - print(f"Processed: {job.metadata.completed_requests} requests") -``` - -**Usage Example - Inline Requests:** - -```python -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Create batch job with inline requests -requests = [ - {'contents': 'What is AI?'}, - {'contents': 'Explain machine learning'}, - {'contents': 'What is deep learning?'} -] - -job = client.batches.create( - model='gemini-2.0-flash', - src=requests, - dest='gs://my-bucket/inline-results/' -) - -print(f"Job: {job.name}") -``` - -### Create Embeddings Batch Job - -Create a batch job specifically for embedding generation, optimized for generating embeddings for large datasets. - -```python { .api } -class Batches: - """Synchronous batch prediction jobs API.""" - - def create_embeddings( - self, - *, - model: str, - src: Union[str, list[dict]], - dest: Optional[str] = None, - config: Optional[CreateBatchJobConfig] = None - ) -> BatchJob: - """ - Create batch embeddings job. - - Parameters: - model (str): Embedding model (e.g., 'text-embedding-004'). - src (Union[str, list[dict]]): Source of embedding requests. - dest (str, optional): Destination for embeddings. - config (CreateBatchJobConfig, optional): Job configuration. - - Returns: - BatchJob: Created batch embeddings job. - """ - ... - -class AsyncBatches: - """Asynchronous batch prediction jobs API.""" - - async def create_embeddings( - self, - *, - model: str, - src: Union[str, list[dict]], - dest: Optional[str] = None, - config: Optional[CreateBatchJobConfig] = None - ) -> BatchJob: - """Async version of create_embeddings.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -job = client.batches.create_embeddings( - model='text-embedding-004', - src='gs://my-bucket/texts.jsonl', - dest='gs://my-bucket/embeddings/' -) - -print(f"Embeddings job: {job.name}") -``` - -### Get Batch Job - -Retrieve information about a batch job including status and progress. - -```python { .api } -class Batches: - """Synchronous batch prediction jobs API.""" - - def get(self, *, name: str) -> BatchJob: - """ - Get batch job status and information. - - Parameters: - name (str): Job name in format 'projects/*/locations/*/batchPredictionJobs/*'. - - Returns: - BatchJob: Job information including state, progress, and metadata. - """ - ... - -class AsyncBatches: - """Asynchronous batch prediction jobs API.""" - - async def get(self, *, name: str) -> BatchJob: - """Async version of get.""" - ... -``` - -### Cancel Batch Job - -Cancel a running batch job. - -```python { .api } -class Batches: - """Synchronous batch prediction jobs API.""" - - def cancel(self, *, name: str) -> None: - """ - Cancel a batch job. - - Parameters: - name (str): Job name. - """ - ... - -class AsyncBatches: - """Asynchronous batch prediction jobs API.""" - - async def cancel(self, *, name: str) -> None: - """Async version of cancel.""" - ... -``` - -### Delete Batch Job - -Delete a batch job. - -```python { .api } -class Batches: - """Synchronous batch prediction jobs API.""" - - def delete(self, *, name: str) -> None: - """ - Delete a batch job. - - Parameters: - name (str): Job name. - """ - ... - -class AsyncBatches: - """Asynchronous batch prediction jobs API.""" - - async def delete(self, *, name: str) -> None: - """Async version of delete.""" - ... -``` - -### List Batch Jobs - -List all batch jobs with optional filtering and pagination. - -```python { .api } -class Batches: - """Synchronous batch prediction jobs API.""" - - def list( - self, - *, - config: Optional[ListBatchJobsConfig] = None - ) -> Union[Pager[BatchJob], Iterator[BatchJob]]: - """ - List batch jobs. - - Parameters: - config (ListBatchJobsConfig, optional): List configuration including: - - page_size: Number of jobs per page - - page_token: Token for pagination - - filter: Filter expression - - Returns: - Union[Pager[BatchJob], Iterator[BatchJob]]: Paginated job list. - """ - ... - -class AsyncBatches: - """Asynchronous batch prediction jobs API.""" - - async def list( - self, - *, - config: Optional[ListBatchJobsConfig] = None - ) -> Union[AsyncPager[BatchJob], AsyncIterator[BatchJob]]: - """Async version of list.""" - ... -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, Iterator, AsyncIterator, Dict, Any -from datetime import datetime -from enum import Enum - -# Configuration types -class CreateBatchJobConfig: - """ - Configuration for creating batch jobs. - - Attributes: - display_name (str, optional): Display name for the job. - labels (dict[str, str], optional): Labels for the job. - """ - display_name: Optional[str] = None - labels: Optional[dict[str, str]] = None - -class ListBatchJobsConfig: - """ - Configuration for listing batch jobs. - - Attributes: - page_size (int, optional): Number of jobs per page. - page_token (str, optional): Token for pagination. - filter (str, optional): Filter expression (e.g., 'state=JOB_STATE_RUNNING'). - """ - page_size: Optional[int] = None - page_token: Optional[str] = None - filter: Optional[str] = None - -# Response types -class BatchJob: - """ - Batch prediction job information. - - Attributes: - name (str): Job resource name. - display_name (str, optional): Display name. - model (str): Model used for the job. - state (JobState): Current job state. - create_time (datetime): When job was created. - start_time (datetime, optional): When job started running. - end_time (datetime, optional): When job completed. - update_time (datetime): Last update time. - labels (dict[str, str], optional): Job labels. - metadata (BatchJobMetadata, optional): Job metadata and progress. - error (JobError, optional): Error if job failed. - """ - name: str - display_name: Optional[str] = None - model: str - state: JobState - create_time: datetime - start_time: Optional[datetime] = None - end_time: Optional[datetime] = None - update_time: datetime - labels: Optional[dict[str, str]] = None - metadata: Optional[BatchJobMetadata] = None - error: Optional[JobError] = None - -class BatchJobMetadata: - """ - Batch job metadata and progress. - - Attributes: - total_requests (int, optional): Total number of requests. - completed_requests (int, optional): Number of completed requests. - failed_requests (int, optional): Number of failed requests. - input_config (dict, optional): Input configuration. - output_config (dict, optional): Output configuration. - """ - total_requests: Optional[int] = None - completed_requests: Optional[int] = None - failed_requests: Optional[int] = None - input_config: Optional[dict] = None - output_config: Optional[dict] = None - -class JobState(Enum): - """Batch job states.""" - JOB_STATE_UNSPECIFIED = 'JOB_STATE_UNSPECIFIED' - JOB_STATE_QUEUED = 'JOB_STATE_QUEUED' - JOB_STATE_PENDING = 'JOB_STATE_PENDING' - JOB_STATE_RUNNING = 'JOB_STATE_RUNNING' - JOB_STATE_SUCCEEDED = 'JOB_STATE_SUCCEEDED' - JOB_STATE_FAILED = 'JOB_STATE_FAILED' - JOB_STATE_CANCELLING = 'JOB_STATE_CANCELLING' - JOB_STATE_CANCELLED = 'JOB_STATE_CANCELLED' - JOB_STATE_PAUSED = 'JOB_STATE_PAUSED' - JOB_STATE_EXPIRED = 'JOB_STATE_EXPIRED' - -class JobError: - """ - Job error information. - - Attributes: - code (int): Error code. - message (str): Error message. - details (list[dict], optional): Error details. - """ - code: int - message: str - details: Optional[list[dict]] = None - -# Pager types -class Pager[T]: - """Synchronous pager.""" - page: list[T] - def next_page(self) -> None: ... - def __iter__(self) -> Iterator[T]: ... - -class AsyncPager[T]: - """Asynchronous pager.""" - page: list[T] - async def next_page(self) -> None: ... - async def __aiter__(self) -> AsyncIterator[T]: ... -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/caching.md b/.tessl/tiles/tessl/pypi-google-genai/docs/caching.md deleted file mode 100644 index 12bab3f9f..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/caching.md +++ /dev/null @@ -1,455 +0,0 @@ -# Context Caching - -Create and manage cached content to reduce costs and latency for repeated requests with shared context. Context caching stores frequently used content (like large documents or conversation context) so it doesn't need to be sent with every request, significantly reducing token costs and improving response times. - -## Capabilities - -### Create Cached Content - -Create a new cached content resource containing frequently used context. - -```python { .api } -class Caches: - """Synchronous cached content management API.""" - - def create( - self, - *, - model: str, - config: CreateCachedContentConfig - ) -> CachedContent: - """ - Create cached content for reuse across requests. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). - config (CreateCachedContentConfig): Cache configuration including: - - contents: Content to cache (documents, conversation history, etc.) - - system_instruction: System instruction to cache - - tools: Tool definitions to cache - - ttl: Time-to-live duration (e.g., '3600s' for 1 hour) - - expire_time: Absolute expiration time - - display_name: Display name for the cache - - Returns: - CachedContent: Created cache with name, expiration info, and metadata. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncCaches: - """Asynchronous cached content management API.""" - - async def create( - self, - *, - model: str, - config: CreateCachedContentConfig - ) -> CachedContent: - """Async version of create.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import ( - CreateCachedContentConfig, - Content, - Part -) - -client = Client(api_key='YOUR_API_KEY') - -# Create large document content -document = """ -[Large document text here - e.g., 50,000 tokens of product documentation] -""" - -config = CreateCachedContentConfig( - contents=[Content(parts=[Part(text=document)])], - system_instruction='You are a product support assistant.', - ttl='3600s', # Cache for 1 hour - display_name='Product Documentation Cache' -) - -cache = client.caches.create( - model='gemini-2.0-flash', - config=config -) - -print(f"Created cache: {cache.name}") -print(f"Expires: {cache.expire_time}") -print(f"Usage: {cache.usage_metadata.total_token_count} tokens") - -# Use cache in requests -from google.genai.types import GenerateContentConfig - -gen_config = GenerateContentConfig( - cached_content=cache.name -) - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='What is the return policy?', - config=gen_config -) -print(response.text) -``` - -### Get Cached Content - -Retrieve information about a cached content resource. - -```python { .api } -class Caches: - """Synchronous cached content management API.""" - - def get(self, *, name: str) -> CachedContent: - """ - Get cached content information. - - Parameters: - name (str): Cache name in format 'cachedContents/{cache_id}'. - - Returns: - CachedContent: Cache information including expiration and usage. - - Raises: - ClientError: For client errors including 404 if cache not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncCaches: - """Asynchronous cached content management API.""" - - async def get(self, *, name: str) -> CachedContent: - """Async version of get.""" - ... -``` - -### Update Cached Content - -Update cached content expiration time or other mutable fields. - -```python { .api } -class Caches: - """Synchronous cached content management API.""" - - def update( - self, - *, - name: str, - config: UpdateCachedContentConfig - ) -> CachedContent: - """ - Update cached content. - - Parameters: - name (str): Cache name in format 'cachedContents/{cache_id}'. - config (UpdateCachedContentConfig): Update configuration including: - - ttl: New time-to-live duration - - expire_time: New absolute expiration time - - Returns: - CachedContent: Updated cache information. - - Raises: - ClientError: For client errors including 404 if cache not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncCaches: - """Asynchronous cached content management API.""" - - async def update( - self, - *, - name: str, - config: UpdateCachedContentConfig - ) -> CachedContent: - """Async version of update.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import UpdateCachedContentConfig - -client = Client(api_key='YOUR_API_KEY') - -# Extend cache expiration -config = UpdateCachedContentConfig( - ttl='7200s' # Extend to 2 hours -) - -updated_cache = client.caches.update( - name='cachedContents/abc123', - config=config -) - -print(f"New expiration: {updated_cache.expire_time}") -``` - -### Delete Cached Content - -Delete a cached content resource. Caches are automatically deleted after expiration, but you can delete them early to free resources. - -```python { .api } -class Caches: - """Synchronous cached content management API.""" - - def delete(self, *, name: str) -> None: - """ - Delete cached content. - - Parameters: - name (str): Cache name in format 'cachedContents/{cache_id}'. - - Raises: - ClientError: For client errors including 404 if cache not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncCaches: - """Asynchronous cached content management API.""" - - async def delete(self, *, name: str) -> None: - """Async version of delete.""" - ... -``` - -### List Cached Contents - -List all cached content resources with optional pagination. - -```python { .api } -class Caches: - """Synchronous cached content management API.""" - - def list( - self, - *, - config: Optional[ListCachedContentsConfig] = None - ) -> Union[Pager[CachedContent], Iterator[CachedContent]]: - """ - List cached contents. - - Parameters: - config (ListCachedContentsConfig, optional): List configuration including: - - page_size: Number of items per page - - page_token: Token for pagination - - Returns: - Union[Pager[CachedContent], Iterator[CachedContent]]: Paginated cache list. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncCaches: - """Asynchronous cached content management API.""" - - async def list( - self, - *, - config: Optional[ListCachedContentsConfig] = None - ) -> Union[AsyncPager[CachedContent], AsyncIterator[CachedContent]]: - """Async version of list.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# List all caches -for cache in client.caches.list(): - print(f"{cache.display_name}: {cache.name}") - print(f" Expires: {cache.expire_time}") - print(f" Tokens: {cache.usage_metadata.total_token_count}") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, Iterator, AsyncIterator -from datetime import datetime -from enum import Enum - -# Configuration types -class CreateCachedContentConfig: - """ - Configuration for creating cached content. - - Attributes: - contents (list[Content]): Content to cache (documents, examples, etc.). - system_instruction (Union[str, Content], optional): System instruction to cache. - tools (list[Tool], optional): Tool definitions to cache. - tool_config (ToolConfig, optional): Tool configuration to cache. - ttl (str, optional): Time-to-live duration (e.g., '3600s', '1h'). Either ttl - or expire_time must be provided. - expire_time (datetime, optional): Absolute expiration time. Either ttl or - expire_time must be provided. - display_name (str, optional): Display name for the cache. - """ - contents: list[Content] - system_instruction: Optional[Union[str, Content]] = None - tools: Optional[list[Tool]] = None - tool_config: Optional[ToolConfig] = None - ttl: Optional[str] = None - expire_time: Optional[datetime] = None - display_name: Optional[str] = None - -class UpdateCachedContentConfig: - """ - Configuration for updating cached content. - - Attributes: - ttl (str, optional): New time-to-live duration. Either ttl or expire_time - must be provided. - expire_time (datetime, optional): New absolute expiration time. - """ - ttl: Optional[str] = None - expire_time: Optional[datetime] = None - -class ListCachedContentsConfig: - """ - Configuration for listing cached contents. - - Attributes: - page_size (int, optional): Number of items per page. - page_token (str, optional): Token for pagination. - """ - page_size: Optional[int] = None - page_token: Optional[str] = None - -# Response types -class CachedContent: - """ - Cached content resource. - - Attributes: - name (str): Resource name in format 'cachedContents/{cache_id}'. - model (str): Model identifier this cache is for. - display_name (str, optional): Display name. - contents (list[Content]): Cached content. - system_instruction (Content, optional): Cached system instruction. - tools (list[Tool], optional): Cached tools. - tool_config (ToolConfig, optional): Cached tool config. - create_time (datetime): When cache was created. - update_time (datetime): When cache was last updated. - expire_time (datetime): When cache will expire. - usage_metadata (CachedContentUsageMetadata): Token usage information. - """ - name: str - model: str - display_name: Optional[str] = None - contents: list[Content] - system_instruction: Optional[Content] = None - tools: Optional[list[Tool]] = None - tool_config: Optional[ToolConfig] = None - create_time: datetime - update_time: datetime - expire_time: datetime - usage_metadata: CachedContentUsageMetadata - -class CachedContentUsageMetadata: - """ - Token usage metadata for cached content. - - Attributes: - total_token_count (int): Total tokens in cached content. - """ - total_token_count: int - -# Core types (from content-generation) -class Content: - """Content container with role and parts.""" - parts: list[Part] - role: Optional[str] = None - -class Part: - """Individual content part.""" - text: Optional[str] = None - inline_data: Optional[Blob] = None - file_data: Optional[FileData] = None - -class Blob: - """Binary data with MIME type.""" - mime_type: str - data: bytes - -class FileData: - """Reference to uploaded file.""" - file_uri: str - mime_type: str - -class Tool: - """Tool with function declarations.""" - function_declarations: Optional[list[FunctionDeclaration]] = None - -class FunctionDeclaration: - """Function definition.""" - name: str - description: str - parameters: Optional[Schema] = None - -class ToolConfig: - """Tool configuration.""" - function_calling_config: Optional[FunctionCallingConfig] = None - -class FunctionCallingConfig: - """Function calling mode configuration.""" - mode: FunctionCallingConfigMode - allowed_function_names: Optional[list[str]] = None - -class FunctionCallingConfigMode(Enum): - """Function calling modes.""" - MODE_UNSPECIFIED = 'MODE_UNSPECIFIED' - AUTO = 'AUTO' - ANY = 'ANY' - NONE = 'NONE' - -class Schema: - """JSON schema.""" - type: Type - properties: Optional[dict[str, Schema]] = None - -class Type(Enum): - """JSON schema types.""" - TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED' - STRING = 'STRING' - NUMBER = 'NUMBER' - INTEGER = 'INTEGER' - BOOLEAN = 'BOOLEAN' - ARRAY = 'ARRAY' - OBJECT = 'OBJECT' - -# Pager types -class Pager[T]: - """Synchronous pager.""" - page: list[T] - def next_page(self) -> None: ... - def __iter__(self) -> Iterator[T]: ... - -class AsyncPager[T]: - """Asynchronous pager.""" - page: list[T] - async def next_page(self) -> None: ... - async def __aiter__(self) -> AsyncIterator[T]: ... -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/chats.md b/.tessl/tiles/tessl/pypi-google-genai/docs/chats.md deleted file mode 100644 index b5b47cdca..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/chats.md +++ /dev/null @@ -1,738 +0,0 @@ -# Multi-Turn Conversations - -Create and manage chat sessions for multi-turn conversations with automatic history management. Chat sessions maintain conversation context and provide a convenient interface for back-and-forth interactions with the model. - -## Capabilities - -### Create Chat Session - -Create a new chat session with optional configuration and initial history. The chat session automatically manages conversation history across multiple turns. - -```python { .api } -class Chats: - """Factory for creating synchronous chat sessions.""" - - def create( - self, - *, - model: str, - config: Optional[GenerateContentConfig] = None, - history: Optional[list[Content]] = None - ) -> Chat: - """ - Create a new chat session. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). - config (GenerateContentConfig, optional): Default configuration for all messages - in this chat. Includes system instructions, generation config, safety settings, - tools, etc. Can be overridden per message. - history (list[Content], optional): Initial conversation history. Each Content - should have a role ('user' or 'model') and parts. - - Returns: - Chat: New chat session instance for sending messages. - """ - ... - -class AsyncChats: - """Factory for creating asynchronous chat sessions.""" - - def create( - self, - *, - model: str, - config: Optional[GenerateContentConfig] = None, - history: Optional[list[Content]] = None - ) -> AsyncChat: - """ - Create a new async chat session. - - Parameters: - model (str): Model identifier. - config (GenerateContentConfig, optional): Default configuration. - history (list[Content], optional): Initial conversation history. - - Returns: - AsyncChat: New async chat session instance. - """ - ... -``` - -**Usage Example - Create Chat:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Create chat session -chat = client.chats.create(model='gemini-2.0-flash') - -# Send messages -response1 = chat.send_message('What is machine learning?') -print(response1.text) - -response2 = chat.send_message('Can you give me an example?') -print(response2.text) - -# History is automatically maintained -history = chat.get_history() -print(f"Conversation has {len(history)} messages") -``` - -**Usage Example - With Configuration:** - -```python -from google.genai import Client -from google.genai.types import ( - GenerateContentConfig, - SafetySetting, - HarmCategory, - HarmBlockThreshold -) - -client = Client(api_key='YOUR_API_KEY') - -config = GenerateContentConfig( - system_instruction='You are a helpful coding assistant.', - temperature=0.3, - max_output_tokens=512, - safety_settings=[ - SafetySetting( - category=HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, - threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE - ) - ] -) - -chat = client.chats.create( - model='gemini-2.0-flash', - config=config -) - -response = chat.send_message('How do I sort a list in Python?') -print(response.text) -``` - -### Send Message - -Send a message to the chat session and receive a response. The message and response are automatically added to the conversation history. - -```python { .api } -class Chat: - """Synchronous chat session for multi-turn conversations.""" - - def send_message( - self, - message: Union[str, Part, Image, list[Part]], - config: Optional[GenerateContentConfig] = None - ) -> GenerateContentResponse: - """ - Send a message to the chat and receive a response. - - Parameters: - message (Union[str, Part, Image, list[Part]]): Message to send. Can be: - - str: Simple text message - - Part: Single part (text, inline_data, etc.) - - Image: Image to send with implicit question - - list[Part]: Multiple parts (e.g., text + image) - config (GenerateContentConfig, optional): Configuration override for this - message only. Merged with chat's default config. - - Returns: - GenerateContentResponse: Model response containing generated content, - usage metadata, and safety ratings. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncChat: - """Asynchronous chat session for multi-turn conversations.""" - - async def send_message( - self, - message: Union[str, Part, Image, list[Part]], - config: Optional[GenerateContentConfig] = None - ) -> GenerateContentResponse: - """ - Async version of send_message. - - Parameters: - message (Union[str, Part, Image, list[Part]]): Message to send. - config (GenerateContentConfig, optional): Configuration override. - - Returns: - GenerateContentResponse: Model response. - """ - ... -``` - -**Usage Example - Send Text Message:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') -chat = client.chats.create(model='gemini-2.0-flash') - -# Send text messages -response1 = chat.send_message('Tell me about Python.') -print(response1.text) - -response2 = chat.send_message('What are its main features?') -print(response2.text) -``` - -**Usage Example - Send Multimodal Message:** - -```python -from google.genai import Client -from google.genai.types import Part, Image - -client = Client(api_key='YOUR_API_KEY') -chat = client.chats.create(model='gemini-2.0-flash') - -# Send image with question -image = Image.from_file('diagram.png') -message_parts = [ - Part(text='What does this diagram show?'), - Part(inline_data=image.blob) -] - -response = chat.send_message(message_parts) -print(response.text) - -# Continue conversation -followup = chat.send_message('Can you explain it in more detail?') -print(followup.text) -``` - -### Send Message Streaming - -Send a message and receive the response as a stream of chunks, allowing for real-time display of the model's output. - -```python { .api } -class Chat: - """Synchronous chat session for multi-turn conversations.""" - - def send_message_stream( - self, - message: Union[str, Part, Image, list[Part]], - config: Optional[GenerateContentConfig] = None - ) -> Iterator[GenerateContentResponse]: - """ - Send a message to the chat and receive a streaming response. - - Parameters: - message (Union[str, Part, Image, list[Part]]): Message to send. - config (GenerateContentConfig, optional): Configuration override for this message. - - Yields: - GenerateContentResponse: Streaming response chunks as they are generated. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncChat: - """Asynchronous chat session for multi-turn conversations.""" - - async def send_message_stream( - self, - message: Union[str, Part, Image, list[Part]], - config: Optional[GenerateContentConfig] = None - ) -> AsyncIterator[GenerateContentResponse]: - """ - Async version of send_message_stream. - - Parameters: - message (Union[str, Part, Image, list[Part]]): Message to send. - config (GenerateContentConfig, optional): Configuration override. - - Yields: - GenerateContentResponse: Streaming response chunks. - """ - ... -``` - -**Usage Example - Streaming:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') -chat = client.chats.create(model='gemini-2.0-flash') - -# First message -response1 = chat.send_message('What is neural network?') -print(response1.text) - -# Streaming follow-up -print('\nModel response (streaming):') -stream = chat.send_message_stream('Explain backpropagation.') -for chunk in stream: - print(chunk.text, end='', flush=True) -print() # New line -``` - -**Usage Example - Async Streaming:** - -```python -import asyncio -from google.genai import Client - -async def main(): - client = Client(api_key='YOUR_API_KEY') - chat = client.aio.chats.create(model='gemini-2.0-flash') - - # First message - response = await chat.send_message('Hello!') - print(response.text) - - # Streaming follow-up - print('\nStreaming response:') - stream = await chat.send_message_stream('Tell me about AI.') - async for chunk in stream: - print(chunk.text, end='', flush=True) - print() - -asyncio.run(main()) -``` - -### Get History - -Retrieve the conversation history for the chat session. - -```python { .api } -class Chat: - """Synchronous chat session for multi-turn conversations.""" - - def get_history(self, curated: bool = False) -> list[Content]: - """ - Get the chat conversation history. - - Parameters: - curated (bool): If True, returns only user and model messages, filtering out - intermediate function calls and responses. If False, returns complete - history including all function calling interactions. Defaults to False. - - Returns: - list[Content]: List of Content objects representing the conversation history - in chronological order. Each Content has a role ('user' or 'model') and - list of parts. - """ - ... - -class AsyncChat: - """Asynchronous chat session for multi-turn conversations.""" - - def get_history(self, curated: bool = False) -> list[Content]: - """ - Get the chat conversation history. - - Note: This is a synchronous method even in AsyncChat as it doesn't require I/O. - - Parameters: - curated (bool): If True, returns curated history without function calls. - - Returns: - list[Content]: Conversation history. - """ - ... -``` - -**Usage Example - Get History:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') -chat = client.chats.create(model='gemini-2.0-flash') - -# Have a conversation -chat.send_message('What is Python?') -chat.send_message('What are its uses?') -chat.send_message('Is it beginner-friendly?') - -# Get full history -history = chat.get_history() -print(f"Total messages: {len(history)}") - -for i, content in enumerate(history): - role = content.role or 'unknown' - text = content.parts[0].text if content.parts else '' - print(f"{i+1}. {role}: {text[:50]}...") - -# Get curated history (without function call details) -curated_history = chat.get_history(curated=True) -print(f"Curated messages: {len(curated_history)}") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, Iterator, AsyncIterator, Any -from enum import Enum - -# Core types (shared with content-generation.md) -class Content: - """ - Container for conversation content with role and parts. - - Attributes: - parts (list[Part]): List of content parts - role (str, optional): Role ('user' or 'model') - """ - parts: list[Part] - role: Optional[str] = None - -class Part: - """ - Individual content part. - - Attributes: - text (str, optional): Text content - inline_data (Blob, optional): Inline binary data - file_data (FileData, optional): Reference to uploaded file - function_call (FunctionCall, optional): Function call from model - function_response (FunctionResponse, optional): Function execution result - executable_code (ExecutableCode, optional): Executable code - code_execution_result (CodeExecutionResult, optional): Code execution output - """ - text: Optional[str] = None - inline_data: Optional[Blob] = None - file_data: Optional[FileData] = None - function_call: Optional[FunctionCall] = None - function_response: Optional[FunctionResponse] = None - executable_code: Optional[ExecutableCode] = None - code_execution_result: Optional[CodeExecutionResult] = None - -class Blob: - """ - Binary data with MIME type. - - Attributes: - mime_type (str): MIME type - data (bytes): Binary data - """ - mime_type: str - data: bytes - -class FileData: - """ - Reference to uploaded file. - - Attributes: - file_uri (str): URI of uploaded file - mime_type (str): MIME type - """ - file_uri: str - mime_type: str - -class Image: - """Image data supporting multiple input formats.""" - pass - -class GenerateContentConfig: - """ - Configuration for content generation. - - Attributes: - system_instruction (Union[str, Content], optional): System instructions - contents (Union[str, list[Content], Content], optional): Override contents - generation_config (GenerationConfig, optional): Generation parameters - safety_settings (list[SafetySetting], optional): Safety filtering - tools (list[Tool], optional): Function declarations - tool_config (ToolConfig, optional): Function calling config - cached_content (str, optional): Cached content reference - """ - system_instruction: Optional[Union[str, Content]] = None - contents: Optional[Union[str, list[Content], Content]] = None - generation_config: Optional[GenerationConfig] = None - safety_settings: Optional[list[SafetySetting]] = None - tools: Optional[list[Tool]] = None - tool_config: Optional[ToolConfig] = None - cached_content: Optional[str] = None - -class GenerationConfig: - """ - Core generation parameters. - - Attributes: - temperature (float, optional): Sampling temperature (0.0-2.0) - top_p (float, optional): Nucleus sampling (0.0-1.0) - top_k (int, optional): Top-k sampling - max_output_tokens (int, optional): Maximum response tokens - stop_sequences (list[str], optional): Stop sequences - """ - temperature: Optional[float] = None - top_p: Optional[float] = None - top_k: Optional[int] = None - max_output_tokens: Optional[int] = None - stop_sequences: Optional[list[str]] = None - -class GenerateContentResponse: - """ - Response from content generation. - - Attributes: - text (str): Text from first candidate - candidates (list[Candidate]): Generated candidates - usage_metadata (GenerateContentResponseUsageMetadata, optional): Usage stats - prompt_feedback (GenerateContentResponsePromptFeedback, optional): Prompt feedback - model_version (str, optional): Model version - """ - text: str - candidates: list[Candidate] - usage_metadata: Optional[GenerateContentResponseUsageMetadata] = None - prompt_feedback: Optional[GenerateContentResponsePromptFeedback] = None - model_version: Optional[str] = None - -class Candidate: - """ - Generated candidate. - - Attributes: - content (Content): Generated content - finish_reason (FinishReason, optional): Reason generation stopped - safety_ratings (list[SafetyRating], optional): Safety ratings - citation_metadata (CitationMetadata, optional): Citations - grounding_metadata (GroundingMetadata, optional): Grounding attribution - """ - content: Content - finish_reason: Optional[FinishReason] = None - safety_ratings: Optional[list[SafetyRating]] = None - citation_metadata: Optional[CitationMetadata] = None - grounding_metadata: Optional[GroundingMetadata] = None - -class GenerateContentResponseUsageMetadata: - """ - Token usage statistics. - - Attributes: - prompt_token_count (int): Prompt tokens - candidates_token_count (int): Generated tokens - total_token_count (int): Total tokens - cached_content_token_count (int, optional): Cached tokens - """ - prompt_token_count: int - candidates_token_count: int - total_token_count: int - cached_content_token_count: Optional[int] = None - -class GenerateContentResponsePromptFeedback: - """ - Prompt feedback. - - Attributes: - block_reason (BlockedReason, optional): Block reason - safety_ratings (list[SafetyRating], optional): Safety ratings - """ - block_reason: Optional[BlockedReason] = None - safety_ratings: Optional[list[SafetyRating]] = None - -class SafetySetting: - """ - Safety filter configuration. - - Attributes: - category (HarmCategory): Harm category - threshold (HarmBlockThreshold): Blocking threshold - """ - category: HarmCategory - threshold: HarmBlockThreshold - -class SafetyRating: - """ - Safety rating. - - Attributes: - category (HarmCategory): Harm category - probability (HarmProbability): Harm probability - blocked (bool): Whether blocked - """ - category: HarmCategory - probability: HarmProbability - blocked: bool - -class Tool: - """ - Tool with function declarations. - - Attributes: - function_declarations (list[FunctionDeclaration], optional): Functions - google_search (GoogleSearch, optional): Google Search tool - code_execution (ToolCodeExecution, optional): Code execution tool - """ - function_declarations: Optional[list[FunctionDeclaration]] = None - google_search: Optional[GoogleSearch] = None - code_execution: Optional[ToolCodeExecution] = None - -class FunctionDeclaration: - """ - Function definition. - - Attributes: - name (str): Function name - description (str): Function description - parameters (Schema, optional): Parameter schema - """ - name: str - description: str - parameters: Optional[Schema] = None - -class FunctionCall: - """ - Function call from model. - - Attributes: - name (str): Function name - args (dict[str, Any]): Arguments - id (str, optional): Call ID - """ - name: str - args: dict[str, Any] - id: Optional[str] = None - -class FunctionResponse: - """ - Function execution result. - - Attributes: - name (str): Function name - response (dict[str, Any]): Return value - id (str, optional): Call ID - """ - name: str - response: dict[str, Any] - id: Optional[str] = None - -class ToolConfig: - """Function calling configuration.""" - function_calling_config: Optional[FunctionCallingConfig] = None - -class FunctionCallingConfig: - """ - Function calling mode. - - Attributes: - mode (FunctionCallingConfigMode): Calling mode - allowed_function_names (list[str], optional): Allowed functions - """ - mode: FunctionCallingConfigMode - allowed_function_names: Optional[list[str]] = None - -class FunctionCallingConfigMode(Enum): - """Function calling modes.""" - MODE_UNSPECIFIED = 'MODE_UNSPECIFIED' - AUTO = 'AUTO' - ANY = 'ANY' - NONE = 'NONE' - -class Schema: - """JSON schema for parameters.""" - type: Type - properties: Optional[dict[str, Schema]] = None - required: Optional[list[str]] = None - -class Type(Enum): - """JSON schema types.""" - TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED' - STRING = 'STRING' - NUMBER = 'NUMBER' - INTEGER = 'INTEGER' - BOOLEAN = 'BOOLEAN' - ARRAY = 'ARRAY' - OBJECT = 'OBJECT' - -class FinishReason(Enum): - """Finish reasons.""" - FINISH_REASON_UNSPECIFIED = 'FINISH_REASON_UNSPECIFIED' - STOP = 'STOP' - MAX_TOKENS = 'MAX_TOKENS' - SAFETY = 'SAFETY' - RECITATION = 'RECITATION' - -class BlockedReason(Enum): - """Blocked reasons.""" - BLOCKED_REASON_UNSPECIFIED = 'BLOCKED_REASON_UNSPECIFIED' - SAFETY = 'SAFETY' - OTHER = 'OTHER' - -class HarmCategory(Enum): - """Harm categories.""" - HARM_CATEGORY_UNSPECIFIED = 'HARM_CATEGORY_UNSPECIFIED' - HARM_CATEGORY_HARASSMENT = 'HARM_CATEGORY_HARASSMENT' - HARM_CATEGORY_HATE_SPEECH = 'HARM_CATEGORY_HATE_SPEECH' - HARM_CATEGORY_SEXUALLY_EXPLICIT = 'HARM_CATEGORY_SEXUALLY_EXPLICIT' - HARM_CATEGORY_DANGEROUS_CONTENT = 'HARM_CATEGORY_DANGEROUS_CONTENT' - -class HarmBlockThreshold(Enum): - """Block thresholds.""" - HARM_BLOCK_THRESHOLD_UNSPECIFIED = 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' - BLOCK_LOW_AND_ABOVE = 'BLOCK_LOW_AND_ABOVE' - BLOCK_MEDIUM_AND_ABOVE = 'BLOCK_MEDIUM_AND_ABOVE' - BLOCK_ONLY_HIGH = 'BLOCK_ONLY_HIGH' - BLOCK_NONE = 'BLOCK_NONE' - -class HarmProbability(Enum): - """Harm probabilities.""" - HARM_PROBABILITY_UNSPECIFIED = 'HARM_PROBABILITY_UNSPECIFIED' - NEGLIGIBLE = 'NEGLIGIBLE' - LOW = 'LOW' - MEDIUM = 'MEDIUM' - HIGH = 'HIGH' - -class CitationMetadata: - """Citation information.""" - citations: list[Citation] - -class Citation: - """Individual citation.""" - start_index: int - end_index: int - uri: str - -class GroundingMetadata: - """Grounding attribution.""" - grounding_chunks: Optional[list[GroundingChunk]] = None - -class GroundingChunk: - """Grounding chunk.""" - pass - -class GoogleSearch: - """Google Search tool.""" - pass - -class ToolCodeExecution: - """Code execution tool.""" - pass - -class ExecutableCode: - """Executable code from model.""" - language: Language - code: str - -class Language(Enum): - """Programming languages.""" - LANGUAGE_UNSPECIFIED = 'LANGUAGE_UNSPECIFIED' - PYTHON = 'PYTHON' - -class CodeExecutionResult: - """Code execution result.""" - outcome: Outcome - output: str - -class Outcome(Enum): - """Execution outcomes.""" - OUTCOME_UNSPECIFIED = 'OUTCOME_UNSPECIFIED' - OUTCOME_OK = 'OUTCOME_OK' - OUTCOME_FAILED = 'OUTCOME_FAILED' -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/client.md b/.tessl/tiles/tessl/pypi-google-genai/docs/client.md deleted file mode 100644 index a6d606510..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/client.md +++ /dev/null @@ -1,463 +0,0 @@ -# Client Initialization and Configuration - -Client initialization and configuration for accessing the Google GenAI Python SDK. The SDK supports both the Gemini Developer API (using API keys) and Vertex AI API (using Google Cloud credentials and project configuration). The Client class provides access to all SDK functionality through specialized API modules. - -## Capabilities - -### Client Initialization - -Initialize the main synchronous client to access all SDK functionality. The client automatically determines which API to use based on the provided configuration. - -```python { .api } -class Client: - """ - Primary synchronous client for making requests to Gemini Developer API or Vertex AI API. - - Parameters: - vertexai (bool, optional): Use Vertex AI API endpoints. Defaults to False (Gemini Developer API). - Can be set via GOOGLE_GENAI_USE_VERTEXAI environment variable. - api_key (str, optional): API key for Gemini Developer API authentication. - Can be set via GOOGLE_API_KEY environment variable. - credentials (google.auth.credentials.Credentials, optional): Google Cloud credentials for Vertex AI API. - Uses Application Default Credentials if not provided. - project (str, optional): Google Cloud project ID for Vertex AI API quota. - Can be set via GOOGLE_CLOUD_PROJECT environment variable. - location (str, optional): Google Cloud location for Vertex AI API (e.g., 'us-central1'). - Can be set via GOOGLE_CLOUD_LOCATION environment variable. - debug_config (DebugConfig, optional): Configuration for testing and debugging network behavior. - http_options (Union[HttpOptions, HttpOptionsDict], optional): HTTP client configuration options. - - Returns: - Client instance providing access to all SDK API modules. - """ - def __init__( - self, - *, - vertexai: Optional[bool] = None, - api_key: Optional[str] = None, - credentials: Optional[google.auth.credentials.Credentials] = None, - project: Optional[str] = None, - location: Optional[str] = None, - debug_config: Optional[DebugConfig] = None, - http_options: Optional[Union[HttpOptions, HttpOptionsDict]] = None - ): ... - - @property - def aio(self) -> AsyncClient: - """Access the async client interface for non-blocking operations.""" - ... - - @property - def models(self) -> Models: - """Access models API for content generation, embeddings, and image/video generation.""" - ... - - @property - def chats(self) -> Chats: - """Create multi-turn chat sessions with automatic history management.""" - ... - - @property - def files(self) -> Files: - """Access file management API (Gemini Developer API only).""" - ... - - @property - def caches(self) -> Caches: - """Access cached content API for context caching.""" - ... - - @property - def batches(self) -> Batches: - """Access batch prediction jobs API.""" - ... - - @property - def tunings(self) -> Tunings: - """Access tuning jobs API (Vertex AI only).""" - ... - - @property - def file_search_stores(self) -> FileSearchStores: - """Access file search stores API for retrieval-augmented generation.""" - ... - - @property - def auth_tokens(self) -> Tokens: - """Access authentication tokens API.""" - ... - - @property - def operations(self) -> Operations: - """Access long-running operations API.""" - ... - - @property - def vertexai(self) -> bool: - """Returns True if the client is using Vertex AI API, False otherwise.""" - ... - - def close(self) -> None: - """ - Close the synchronous client explicitly to release resources. - - Note: This does not close the async client. Use Client.aio.aclose() for async client. - """ - ... - - def __enter__(self) -> 'Client': - """Context manager entry for automatic resource cleanup.""" - ... - - def __exit__(self, *args) -> None: - """Context manager exit for automatic resource cleanup.""" - ... -``` - -**Usage Example - Gemini Developer API:** - -```python -from google.genai import Client - -# Initialize with API key -client = Client(api_key='YOUR_API_KEY') - -# Generate content -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Explain quantum computing' -) -print(response.text) - -# Close when done -client.close() -``` - -**Usage Example - Context Manager:** - -```python -from google.genai import Client - -with Client(api_key='YOUR_API_KEY') as client: - response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Hello!' - ) - print(response.text) -# Client automatically closes -``` - -**Usage Example - Vertex AI:** - -```python -from google.genai import Client - -# Initialize for Vertex AI -client = Client( - vertexai=True, - project='my-project-id', - location='us-central1' -) - -# Generate content -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Explain machine learning' -) -print(response.text) - -client.close() -``` - -### Async Client - -Access asynchronous (non-blocking) client for concurrent operations. The async client provides the same API modules as the sync client but with async/await support. - -```python { .api } -class AsyncClient: - """ - Asynchronous client for making non-blocking requests. - - Access via Client.aio property. All methods are async and require await. - """ - - @property - def models(self) -> AsyncModels: - """Access async models API.""" - ... - - @property - def chats(self) -> AsyncChats: - """Create async multi-turn chat sessions.""" - ... - - @property - def files(self) -> AsyncFiles: - """Access async file management API.""" - ... - - @property - def caches(self) -> AsyncCaches: - """Access async cached content API.""" - ... - - @property - def batches(self) -> AsyncBatches: - """Access async batch prediction jobs API.""" - ... - - @property - def tunings(self) -> AsyncTunings: - """Access async tuning jobs API.""" - ... - - @property - def file_search_stores(self) -> AsyncFileSearchStores: - """Access async file search stores API.""" - ... - - @property - def live(self) -> AsyncLive: - """Access async live API for real-time bidirectional streaming.""" - ... - - @property - def auth_tokens(self) -> AsyncTokens: - """Access async authentication tokens API.""" - ... - - @property - def operations(self) -> AsyncOperations: - """Access async long-running operations API.""" - ... - - async def aclose(self) -> None: - """ - Close the async client explicitly to release resources. - - Note: This does not close the sync client. Use Client.close() for sync client. - """ - ... - - async def __aenter__(self) -> 'AsyncClient': - """Async context manager entry.""" - ... - - async def __aexit__(self, *args) -> None: - """Async context manager exit.""" - ... -``` - -**Usage Example - Async Client:** - -```python -import asyncio -from google.genai import Client - -async def main(): - client = Client(api_key='YOUR_API_KEY') - async_client = client.aio - - # Make async request - response = await async_client.models.generate_content( - model='gemini-2.0-flash', - contents='What is AI?' - ) - print(response.text) - - # Close async client - await async_client.aclose() - client.close() - -asyncio.run(main()) -``` - -**Usage Example - Async Context Manager:** - -```python -import asyncio -from google.genai import Client - -async def main(): - client = Client(api_key='YOUR_API_KEY') - - async with client.aio as async_client: - response = await async_client.models.generate_content( - model='gemini-2.0-flash', - contents='Hello!' - ) - print(response.text) - - client.close() - -asyncio.run(main()) -``` - -### HTTP Configuration - -Configure HTTP client behavior including API version, timeouts, retries, and custom headers. - -```python { .api } -class HttpOptions: - """ - HTTP client configuration options. - - Parameters: - api_version (str, optional): API version to use (e.g., 'v1', 'v1beta'). Defaults to 'v1beta'. - base_url (str, optional): Override base URL for API requests. - timeout (float, optional): Request timeout in seconds. - headers (Dict[str, str], optional): Additional HTTP headers for all requests. - retry_options (HttpRetryOptions, optional): Retry configuration for failed requests. - """ - api_version: Optional[str] = None - base_url: Optional[str] = None - timeout: Optional[float] = None - headers: Optional[Dict[str, str]] = None - retry_options: Optional[HttpRetryOptions] = None - -class HttpRetryOptions: - """ - HTTP retry configuration for handling transient failures. - - Parameters: - max_retries (int, optional): Maximum number of retry attempts. Defaults to 3. - initial_backoff (float, optional): Initial backoff delay in seconds. Defaults to 1.0. - max_backoff (float, optional): Maximum backoff delay in seconds. Defaults to 60.0. - backoff_multiplier (float, optional): Backoff multiplier for exponential backoff. Defaults to 2.0. - retry_statuses (List[int], optional): HTTP status codes that trigger retries. - Defaults to [408, 429, 500, 502, 503, 504]. - """ - max_retries: Optional[int] = None - initial_backoff: Optional[float] = None - max_backoff: Optional[float] = None - backoff_multiplier: Optional[float] = None - retry_statuses: Optional[List[int]] = None - -# TypedDict variants for flexible usage -class HttpOptionsDict(TypedDict, total=False): - api_version: str - base_url: str - timeout: float - headers: Dict[str, str] - retry_options: Union[HttpRetryOptions, 'HttpRetryOptionsDict'] - -class HttpRetryOptionsDict(TypedDict, total=False): - max_retries: int - initial_backoff: float - max_backoff: float - backoff_multiplier: float - retry_statuses: List[int] -``` - -**Usage Example - HTTP Configuration:** - -```python -from google.genai import Client -from google.genai.types import HttpOptions, HttpRetryOptions - -# Configure HTTP options -http_options = HttpOptions( - api_version='v1', - timeout=30.0, - headers={'User-Agent': 'MyApp/1.0'}, - retry_options=HttpRetryOptions( - max_retries=5, - initial_backoff=2.0, - max_backoff=120.0 - ) -) - -client = Client( - api_key='YOUR_API_KEY', - http_options=http_options -) - -# All requests use these HTTP options -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Hello' -) -``` - -### Debug Configuration - -Configure client behavior for testing and debugging, including record/replay functionality for deterministic testing. - -```python { .api } -class DebugConfig: - """ - Configuration options for testing and debugging client network behavior. - - Parameters: - client_mode (str, optional): Client operation mode. Options: - - 'record': Record API requests and responses to replay files - - 'replay': Replay API responses from recorded files - - 'auto': Automatically record if replay file doesn't exist, otherwise replay - Can be set via GOOGLE_GENAI_CLIENT_MODE environment variable. - replays_directory (str, optional): Directory for storing replay files. - Can be set via GOOGLE_GENAI_REPLAYS_DIRECTORY environment variable. - replay_id (str, optional): Identifier for replay file. - Can be set via GOOGLE_GENAI_REPLAY_ID environment variable. - """ - client_mode: Optional[str] = None - replays_directory: Optional[str] = None - replay_id: Optional[str] = None -``` - -**Usage Example - Debug Configuration:** - -```python -from google.genai import Client -from google.genai.client import DebugConfig - -# Configure for recording API interactions -debug_config = DebugConfig( - client_mode='record', - replays_directory='./test_replays', - replay_id='test_001' -) - -client = Client( - api_key='YOUR_API_KEY', - debug_config=debug_config -) - -# Requests are recorded to ./test_replays/test_001.json -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Test prompt' -) - -# Later, replay the recorded interactions -debug_config = DebugConfig( - client_mode='replay', - replays_directory='./test_replays', - replay_id='test_001' -) - -# Responses are replayed from recorded file -``` - -## Types - -```python { .api } -from typing import Optional, Union, Dict, List, TypedDict -import google.auth.credentials - -# Credentials type from google.auth -Credentials = google.auth.credentials.Credentials - -# HTTP response metadata -class HttpResponse: - """ - HTTP response metadata from API requests. - - Attributes: - status_code (int): HTTP status code - headers (Dict[str, str]): Response headers - request_url (str): URL of the request - """ - status_code: int - headers: Dict[str, str] - request_url: str -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md b/.tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md deleted file mode 100644 index 1895eed5e..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/content-generation.md +++ /dev/null @@ -1,986 +0,0 @@ -# Content Generation - -Generate text and multimodal content using Gemini models with support for streaming, function calling, structured output, safety controls, and extensive configuration options. Content generation is the primary capability for creating AI-generated responses from text, image, audio, video, and document inputs. - -## Capabilities - -### Generate Content - -Generate content synchronously from text and multimodal inputs. Supports single or multiple content inputs, system instructions, function calling, structured output, and safety settings. - -```python { .api } -def generate_content( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[GenerateContentConfig] = None -) -> GenerateContentResponse: - """ - Generate content from the model. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). - contents (Union[str, list[Content], Content]): Input content. Can be: - - str: Simple text prompt - - Content: Single content object with role and parts - - list[Content]: Multiple content objects for conversation history - config (GenerateContentConfig, optional): Generation configuration including: - - system_instruction: System-level instructions for the model - - generation_config: Parameters like temperature, top_p, max_tokens - - safety_settings: Content safety filtering configuration - - tools: Function declarations for function calling - - tool_config: Function calling behavior configuration - - cached_content: Reference to cached content for efficiency - - Returns: - GenerateContentResponse: Response containing generated content, usage metadata, - and safety ratings. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def generate_content( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[GenerateContentConfig] = None -) -> GenerateContentResponse: - """Async version of generate_content.""" - ... -``` - -**Usage Example - Simple Text Generation:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Explain quantum computing in simple terms.' -) - -print(response.text) -``` - -**Usage Example - With Configuration:** - -```python -from google.genai import Client -from google.genai.types import GenerateContentConfig - -client = Client(api_key='YOUR_API_KEY') - -config = GenerateContentConfig( - system_instruction='You are a helpful physics tutor.', - temperature=0.7, - top_p=0.95, - max_output_tokens=1024, - stop_sequences=['END'] -) - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Explain relativity', - config=config -) - -print(response.text) -``` - -**Usage Example - Multimodal Input:** - -```python -from google.genai import Client -from google.genai.types import Content, Part, Image - -client = Client(api_key='YOUR_API_KEY') - -# Create multimodal content -image = Image.from_file('photo.jpg') -content = Content( - parts=[ - Part(text='What is in this image?'), - Part(inline_data=image.blob) - ] -) - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents=content -) - -print(response.text) -``` - -### Generate Content Streaming - -Generate content with streaming responses, allowing you to receive and process chunks as they are generated rather than waiting for the complete response. - -```python { .api } -def generate_content_stream( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[GenerateContentConfig] = None -) -> Iterator[GenerateContentResponse]: - """ - Generate content in streaming mode, yielding response chunks as they are generated. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). - contents (Union[str, list[Content], Content]): Input content. - config (GenerateContentConfig, optional): Generation configuration. - - Yields: - GenerateContentResponse: Streaming response chunks. Each chunk contains: - - Partial or complete candidates with generated content - - Incremental usage metadata - - Safety ratings - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def generate_content_stream( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[GenerateContentConfig] = None -) -> AsyncIterator[GenerateContentResponse]: - """Async version of generate_content_stream.""" - ... -``` - -**Usage Example - Streaming:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -stream = client.models.generate_content_stream( - model='gemini-2.0-flash', - contents='Write a short story about a robot.' -) - -for chunk in stream: - print(chunk.text, end='', flush=True) -print() # New line after streaming completes -``` - -**Usage Example - Async Streaming:** - -```python -import asyncio -from google.genai import Client - -async def main(): - client = Client(api_key='YOUR_API_KEY') - - stream = await client.aio.models.generate_content_stream( - model='gemini-2.0-flash', - contents='Explain neural networks.' - ) - - async for chunk in stream: - print(chunk.text, end='', flush=True) - print() - -asyncio.run(main()) -``` - -### Function Calling - -Enable the model to call functions you define, allowing it to access external tools, APIs, and data sources. The model generates function calls based on the conversation context, and you execute them and return results. - -**Usage Example - Function Calling:** - -```python -from google.genai import Client -from google.genai.types import ( - GenerateContentConfig, - Tool, - FunctionDeclaration, - Schema, - Type, - FunctionResponse, - Content, - Part -) - -client = Client(api_key='YOUR_API_KEY') - -# Define function declarations -get_weather = FunctionDeclaration( - name='get_weather', - description='Get the current weather for a location', - parameters=Schema( - type=Type.OBJECT, - properties={ - 'location': Schema(type=Type.STRING, description='City name'), - 'unit': Schema( - type=Type.STRING, - enum=['celsius', 'fahrenheit'], - description='Temperature unit' - ) - }, - required=['location'] - ) -) - -config = GenerateContentConfig( - tools=[Tool(function_declarations=[get_weather])] -) - -# Initial request -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='What is the weather in Tokyo?', - config=config -) - -# Check if model wants to call function -if response.candidates[0].content.parts[0].function_call: - function_call = response.candidates[0].content.parts[0].function_call - - # Execute function (example) - if function_call.name == 'get_weather': - weather_data = {'temperature': 22, 'condition': 'sunny'} - - # Send function response back - function_response = FunctionResponse( - name='get_weather', - response=weather_data - ) - - # Continue conversation with function result - response2 = client.models.generate_content( - model='gemini-2.0-flash', - contents=[ - response.candidates[0].content, - Content(parts=[Part(function_response=function_response)]) - ], - config=config - ) - - print(response2.text) -``` - -### Structured Output - -Generate structured output conforming to a JSON schema, ensuring the model's response follows a specific format. - -**Usage Example - Structured Output:** - -```python -from google.genai import Client -from google.genai.types import ( - GenerateContentConfig, - Schema, - Type -) - -client = Client(api_key='YOUR_API_KEY') - -# Define output schema -recipe_schema = Schema( - type=Type.OBJECT, - properties={ - 'recipe_name': Schema(type=Type.STRING), - 'ingredients': Schema( - type=Type.ARRAY, - items=Schema(type=Type.STRING) - ), - 'steps': Schema( - type=Type.ARRAY, - items=Schema(type=Type.STRING) - ), - 'prep_time_minutes': Schema(type=Type.INTEGER) - }, - required=['recipe_name', 'ingredients', 'steps'] -) - -config = GenerateContentConfig( - response_mime_type='application/json', - response_schema=recipe_schema -) - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Give me a recipe for chocolate chip cookies', - config=config -) - -import json -recipe = json.loads(response.text) -print(recipe['recipe_name']) -print(f"Prep time: {recipe['prep_time_minutes']} minutes") -``` - -### Safety Settings - -Configure content safety filtering to control what types of content are blocked or allowed in both inputs and outputs. - -**Usage Example - Safety Settings:** - -```python -from google.genai import Client -from google.genai.types import ( - GenerateContentConfig, - SafetySetting, - HarmCategory, - HarmBlockThreshold -) - -client = Client(api_key='YOUR_API_KEY') - -config = GenerateContentConfig( - safety_settings=[ - SafetySetting( - category=HarmCategory.HARM_CATEGORY_HARASSMENT, - threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE - ), - SafetySetting( - category=HarmCategory.HARM_CATEGORY_HATE_SPEECH, - threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE - ), - SafetySetting( - category=HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, - threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE - ), - SafetySetting( - category=HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, - threshold=HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE - ) - ] -) - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Your prompt here', - config=config -) - -# Check safety ratings -for rating in response.candidates[0].safety_ratings: - print(f"{rating.category}: {rating.probability}") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, Sequence, Any, Dict, Iterator, AsyncIterator -from enum import Enum - -# Core content types -class Content: - """ - Container for conversation content with role and parts. - - Attributes: - parts (list[Part]): List of content parts (text, images, function calls, etc.) - role (str, optional): Role of the content creator ('user' or 'model') - """ - parts: list[Part] - role: Optional[str] = None - -class Part: - """ - Individual content part within a Content object. - - Attributes: - text (str, optional): Text content - inline_data (Blob, optional): Inline binary data (images, audio, etc.) - file_data (FileData, optional): Reference to uploaded file - function_call (FunctionCall, optional): Function call from model - function_response (FunctionResponse, optional): Function execution result - executable_code (ExecutableCode, optional): Executable code from model - code_execution_result (CodeExecutionResult, optional): Code execution output - """ - text: Optional[str] = None - inline_data: Optional[Blob] = None - file_data: Optional[FileData] = None - function_call: Optional[FunctionCall] = None - function_response: Optional[FunctionResponse] = None - executable_code: Optional[ExecutableCode] = None - code_execution_result: Optional[CodeExecutionResult] = None - -class Blob: - """ - Binary data with MIME type. - - Attributes: - mime_type (str): MIME type (e.g., 'image/jpeg', 'audio/wav') - data (bytes): Binary data - """ - mime_type: str - data: bytes - -class FileData: - """ - Reference to an uploaded file. - - Attributes: - file_uri (str): URI of the uploaded file (e.g., 'gs://bucket/file') - mime_type (str): MIME type of the file - """ - file_uri: str - mime_type: str - -class Image: - """ - Image data supporting multiple input formats. - - Can be created from: - - URL: Image.from_url('https://...') - - File path: Image.from_file('path/to/image.jpg') - - Bytes: Image.from_bytes(data, mime_type='image/jpeg') - - PIL Image: Image.from_pil(pil_image) - - FileData: Image(file_data=FileData(...)) - """ - pass - -class Video: - """ - Video data supporting multiple input formats. - - Can be created from: - - URL: Video.from_url('https://...') - - File path: Video.from_file('path/to/video.mp4') - - Bytes: Video.from_bytes(data, mime_type='video/mp4') - - FileData: Video(file_data=FileData(...)) - """ - pass - -# Generation configuration -class GenerateContentConfig: - """ - Configuration for content generation. - - Attributes: - system_instruction (Union[str, Content], optional): System-level instructions - contents (Union[str, list[Content], Content], optional): Override input contents - temperature (float, optional): Sampling temperature (0.0-2.0). Higher = more random. - top_p (float, optional): Nucleus sampling threshold (0.0-1.0) - top_k (float, optional): Top-k sampling parameter - candidate_count (int, optional): Number of response candidates to generate - max_output_tokens (int, optional): Maximum tokens in generated response - stop_sequences (list[str], optional): Sequences that stop generation - response_mime_type (str, optional): MIME type for structured output ('application/json') - response_schema (Schema, optional): JSON schema for structured output - presence_penalty (float, optional): Penalty for token presence (-2.0 to 2.0) - frequency_penalty (float, optional): Penalty for token frequency (-2.0 to 2.0) - response_logprobs (bool, optional): Include log probabilities in response - logprobs (int, optional): Number of top logprobs to return per token - safety_settings (list[SafetySetting], optional): Safety filtering configuration - tools (list[Tool], optional): Function declarations for function calling - tool_config (ToolConfig, optional): Function calling behavior configuration - cached_content (str, optional): Reference to cached content by name - """ - system_instruction: Optional[Union[str, Content]] = None - contents: Optional[Union[str, list[Content], Content]] = None - temperature: Optional[float] = None - top_p: Optional[float] = None - top_k: Optional[float] = None - candidate_count: Optional[int] = None - max_output_tokens: Optional[int] = None - stop_sequences: Optional[list[str]] = None - response_mime_type: Optional[str] = None - response_schema: Optional[Schema] = None - presence_penalty: Optional[float] = None - frequency_penalty: Optional[float] = None - response_logprobs: Optional[bool] = None - logprobs: Optional[int] = None - safety_settings: Optional[list[SafetySetting]] = None - tools: Optional[list[Tool]] = None - tool_config: Optional[ToolConfig] = None - cached_content: Optional[str] = None - -class GenerationConfig: - """ - NOTE: This type is not used directly. Generation parameters are passed directly - to GenerateContentConfig, not as a nested GenerationConfig object. - - Core generation parameters controlling model behavior. - - Attributes: - temperature (float, optional): Sampling temperature (0.0-2.0). Higher = more random. - top_p (float, optional): Nucleus sampling threshold (0.0-1.0) - top_k (int, optional): Top-k sampling parameter - candidate_count (int, optional): Number of response candidates to generate - max_output_tokens (int, optional): Maximum tokens in generated response - stop_sequences (list[str], optional): Sequences that stop generation - response_mime_type (str, optional): MIME type for structured output ('application/json') - response_schema (Schema, optional): JSON schema for structured output - presence_penalty (float, optional): Penalty for token presence (-2.0 to 2.0) - frequency_penalty (float, optional): Penalty for token frequency (-2.0 to 2.0) - response_logprobs (bool, optional): Include log probabilities in response - logprobs (int, optional): Number of top logprobs to return per token - """ - temperature: Optional[float] = None - top_p: Optional[float] = None - top_k: Optional[int] = None - candidate_count: Optional[int] = None - max_output_tokens: Optional[int] = None - stop_sequences: Optional[list[str]] = None - response_mime_type: Optional[str] = None - response_schema: Optional[Schema] = None - presence_penalty: Optional[float] = None - frequency_penalty: Optional[float] = None - response_logprobs: Optional[bool] = None - logprobs: Optional[int] = None - -# Response types -class GenerateContentResponse: - """ - Response from content generation. - - Attributes: - text (str): Convenience property returning text from first candidate - candidates (list[Candidate]): Generated candidates with content and metadata - usage_metadata (GenerateContentResponseUsageMetadata, optional): Token usage stats - prompt_feedback (GenerateContentResponsePromptFeedback, optional): Prompt feedback - model_version (str, optional): Model version used for generation - """ - text: str - candidates: list[Candidate] - usage_metadata: Optional[GenerateContentResponseUsageMetadata] = None - prompt_feedback: Optional[GenerateContentResponsePromptFeedback] = None - model_version: Optional[str] = None - -class Candidate: - """ - Generated candidate with content and metadata. - - Attributes: - content (Content): Generated content - finish_reason (FinishReason, optional): Reason generation stopped - safety_ratings (list[SafetyRating], optional): Safety ratings for content - citation_metadata (CitationMetadata, optional): Citation information - grounding_metadata (GroundingMetadata, optional): Grounding attribution - token_count (int, optional): Number of tokens in candidate - index (int, optional): Candidate index - logprobs_result (LogprobsResult, optional): Log probabilities - """ - content: Content - finish_reason: Optional[FinishReason] = None - safety_ratings: Optional[list[SafetyRating]] = None - citation_metadata: Optional[CitationMetadata] = None - grounding_metadata: Optional[GroundingMetadata] = None - token_count: Optional[int] = None - index: Optional[int] = None - logprobs_result: Optional[LogprobsResult] = None - -class GenerateContentResponseUsageMetadata: - """ - Token usage statistics. - - Attributes: - prompt_token_count (int): Tokens in the prompt - candidates_token_count (int): Tokens in generated candidates - total_token_count (int): Total tokens (prompt + candidates) - cached_content_token_count (int, optional): Tokens from cached content - """ - prompt_token_count: int - candidates_token_count: int - total_token_count: int - cached_content_token_count: Optional[int] = None - -class GenerateContentResponsePromptFeedback: - """ - Feedback about the prompt. - - Attributes: - block_reason (BlockedReason, optional): Reason prompt was blocked - safety_ratings (list[SafetyRating], optional): Safety ratings for prompt - """ - block_reason: Optional[BlockedReason] = None - safety_ratings: Optional[list[SafetyRating]] = None - -# Safety types -class SafetySetting: - """ - Safety filter configuration. - - Attributes: - category (HarmCategory): Harm category to configure - threshold (HarmBlockThreshold): Blocking threshold for this category - method (HarmBlockMethod, optional): Block based on probability or severity - """ - category: HarmCategory - threshold: HarmBlockThreshold - method: Optional[HarmBlockMethod] = None - -class SafetyRating: - """ - Safety rating for content. - - Attributes: - category (HarmCategory): Harm category - probability (HarmProbability): Probability of harm - severity (HarmSeverity, optional): Severity of harm - blocked (bool): Whether content was blocked - """ - category: HarmCategory - probability: HarmProbability - severity: Optional[HarmSeverity] = None - blocked: bool - -class HarmCategory(Enum): - """Harm categories for safety filtering.""" - HARM_CATEGORY_UNSPECIFIED = 'HARM_CATEGORY_UNSPECIFIED' - HARM_CATEGORY_HARASSMENT = 'HARM_CATEGORY_HARASSMENT' - HARM_CATEGORY_HATE_SPEECH = 'HARM_CATEGORY_HATE_SPEECH' - HARM_CATEGORY_SEXUALLY_EXPLICIT = 'HARM_CATEGORY_SEXUALLY_EXPLICIT' - HARM_CATEGORY_DANGEROUS_CONTENT = 'HARM_CATEGORY_DANGEROUS_CONTENT' - HARM_CATEGORY_CIVIC_INTEGRITY = 'HARM_CATEGORY_CIVIC_INTEGRITY' - -class HarmBlockThreshold(Enum): - """Blocking thresholds for safety filtering.""" - HARM_BLOCK_THRESHOLD_UNSPECIFIED = 'HARM_BLOCK_THRESHOLD_UNSPECIFIED' - BLOCK_LOW_AND_ABOVE = 'BLOCK_LOW_AND_ABOVE' - BLOCK_MEDIUM_AND_ABOVE = 'BLOCK_MEDIUM_AND_ABOVE' - BLOCK_ONLY_HIGH = 'BLOCK_ONLY_HIGH' - BLOCK_NONE = 'BLOCK_NONE' - OFF = 'OFF' - -class HarmProbability(Enum): - """Harm probability levels.""" - HARM_PROBABILITY_UNSPECIFIED = 'HARM_PROBABILITY_UNSPECIFIED' - NEGLIGIBLE = 'NEGLIGIBLE' - LOW = 'LOW' - MEDIUM = 'MEDIUM' - HIGH = 'HIGH' - -class HarmSeverity(Enum): - """Harm severity levels.""" - HARM_SEVERITY_UNSPECIFIED = 'HARM_SEVERITY_UNSPECIFIED' - HARM_SEVERITY_NEGLIGIBLE = 'HARM_SEVERITY_NEGLIGIBLE' - HARM_SEVERITY_LOW = 'HARM_SEVERITY_LOW' - HARM_SEVERITY_MEDIUM = 'HARM_SEVERITY_MEDIUM' - HARM_SEVERITY_HIGH = 'HARM_SEVERITY_HIGH' - -class HarmBlockMethod(Enum): - """Block method for safety filtering.""" - HARM_BLOCK_METHOD_UNSPECIFIED = 'HARM_BLOCK_METHOD_UNSPECIFIED' - SEVERITY = 'SEVERITY' - PROBABILITY = 'PROBABILITY' - -class FinishReason(Enum): - """Reasons why model stopped generating.""" - FINISH_REASON_UNSPECIFIED = 'FINISH_REASON_UNSPECIFIED' - STOP = 'STOP' - MAX_TOKENS = 'MAX_TOKENS' - SAFETY = 'SAFETY' - RECITATION = 'RECITATION' - LANGUAGE = 'LANGUAGE' - OTHER = 'OTHER' - BLOCKLIST = 'BLOCKLIST' - PROHIBITED_CONTENT = 'PROHIBITED_CONTENT' - SPII = 'SPII' - MALFORMED_FUNCTION_CALL = 'MALFORMED_FUNCTION_CALL' - -class BlockedReason(Enum): - """Reasons why prompt was blocked.""" - BLOCKED_REASON_UNSPECIFIED = 'BLOCKED_REASON_UNSPECIFIED' - SAFETY = 'SAFETY' - OTHER = 'OTHER' - BLOCKLIST = 'BLOCKLIST' - PROHIBITED_CONTENT = 'PROHIBITED_CONTENT' - -# Function calling types -class Tool: - """ - Tool containing function declarations. - - Attributes: - function_declarations (list[FunctionDeclaration], optional): Function definitions - google_search (GoogleSearch, optional): Google Search tool - code_execution (ToolCodeExecution, optional): Code execution tool - """ - function_declarations: Optional[list[FunctionDeclaration]] = None - google_search: Optional[GoogleSearch] = None - code_execution: Optional[ToolCodeExecution] = None - -class FunctionDeclaration: - """ - Function schema definition for function calling. - - Attributes: - name (str): Function name - description (str): Function description for the model - parameters (Schema, optional): JSON schema for function parameters - """ - name: str - description: str - parameters: Optional[Schema] = None - -class FunctionCall: - """ - Function invocation from model. - - Attributes: - name (str): Function name to call - args (dict[str, Any]): Function arguments - id (str, optional): Unique call identifier - """ - name: str - args: dict[str, Any] - id: Optional[str] = None - -class FunctionResponse: - """ - Function execution response to send back to model. - - Attributes: - name (str): Function name that was called - response (dict[str, Any]): Function return value - id (str, optional): Call identifier matching FunctionCall.id - """ - name: str - response: dict[str, Any] - id: Optional[str] = None - -class ToolConfig: - """ - Configuration for function calling behavior. - - Attributes: - function_calling_config (FunctionCallingConfig, optional): Function calling mode - """ - function_calling_config: Optional[FunctionCallingConfig] = None - -class FunctionCallingConfig: - """ - Function calling mode configuration. - - Attributes: - mode (FunctionCallingConfigMode): Calling mode (AUTO, ANY, NONE) - allowed_function_names (list[str], optional): Restrict to specific functions - """ - mode: FunctionCallingConfigMode - allowed_function_names: Optional[list[str]] = None - -class FunctionCallingConfigMode(Enum): - """Function calling modes.""" - MODE_UNSPECIFIED = 'MODE_UNSPECIFIED' - AUTO = 'AUTO' - ANY = 'ANY' - NONE = 'NONE' - -class Schema: - """ - JSON schema for structured output and function parameters. - - Attributes: - type (Type): Schema type (OBJECT, ARRAY, STRING, NUMBER, etc.) - format (str, optional): Format specifier - description (str, optional): Schema description - nullable (bool, optional): Whether value can be null - enum (list[str], optional): Allowed values for enums - properties (dict[str, Schema], optional): Object properties - required (list[str], optional): Required property names - items (Schema, optional): Schema for array items - """ - type: Type - format: Optional[str] = None - description: Optional[str] = None - nullable: Optional[bool] = None - enum: Optional[list[str]] = None - properties: Optional[dict[str, Schema]] = None - required: Optional[list[str]] = None - items: Optional[Schema] = None - -class Type(Enum): - """JSON schema types.""" - TYPE_UNSPECIFIED = 'TYPE_UNSPECIFIED' - STRING = 'STRING' - NUMBER = 'NUMBER' - INTEGER = 'INTEGER' - BOOLEAN = 'BOOLEAN' - ARRAY = 'ARRAY' - OBJECT = 'OBJECT' - NULL = 'NULL' - -class GoogleSearch: - """Google Search tool for web grounding.""" - pass - -class ToolCodeExecution: - """Code execution tool for running generated code.""" - pass - -class ExecutableCode: - """ - Executable code from model. - - Attributes: - language (Language): Programming language - code (str): Code to execute - """ - language: Language - code: str - -class CodeExecutionResult: - """ - Code execution result. - - Attributes: - outcome (Outcome): Execution outcome (OK, FAILED, DEADLINE_EXCEEDED) - output (str): Execution output - """ - outcome: Outcome - output: str - -class Language(Enum): - """Programming languages for code execution.""" - LANGUAGE_UNSPECIFIED = 'LANGUAGE_UNSPECIFIED' - PYTHON = 'PYTHON' - -class Outcome(Enum): - """Code execution outcomes.""" - OUTCOME_UNSPECIFIED = 'OUTCOME_UNSPECIFIED' - OUTCOME_OK = 'OUTCOME_OK' - OUTCOME_FAILED = 'OUTCOME_FAILED' - OUTCOME_DEADLINE_EXCEEDED = 'OUTCOME_DEADLINE_EXCEEDED' - -# Additional metadata types -class CitationMetadata: - """ - Citation information for generated content. - - Attributes: - citations (list[Citation]): List of citations - """ - citations: list[Citation] - -class Citation: - """ - Individual citation. - - Attributes: - start_index (int): Start character index in generated text - end_index (int): End character index in generated text - uri (str): Citation source URI - title (str, optional): Citation source title - license (str, optional): Content license - publication_date (GoogleTypeDate, optional): Publication date - """ - start_index: int - end_index: int - uri: str - title: Optional[str] = None - license: Optional[str] = None - publication_date: Optional[GoogleTypeDate] = None - -class GroundingMetadata: - """ - Grounding attribution metadata. - - Attributes: - grounding_chunks (list[GroundingChunk], optional): Grounding sources - grounding_supports (list[GroundingSupport], optional): Support scores - web_search_queries (list[str], optional): Web search queries used - search_entry_point (SearchEntryPoint, optional): Search entry point - """ - grounding_chunks: Optional[list[GroundingChunk]] = None - grounding_supports: Optional[list[GroundingSupport]] = None - web_search_queries: Optional[list[str]] = None - search_entry_point: Optional[SearchEntryPoint] = None - -class GroundingChunk: - """Grounding source chunk (web, retrieved context, etc.).""" - pass - -class GroundingSupport: - """ - Grounding support score. - - Attributes: - segment (Segment): Text segment - grounding_chunk_indices (list[int]): Indices of supporting chunks - confidence_scores (list[float]): Confidence scores - """ - segment: Segment - grounding_chunk_indices: list[int] - confidence_scores: list[float] - -class SearchEntryPoint: - """ - Search entry point for web grounding. - - Attributes: - rendered_content (str): Rendered search content - sdk_blob (bytes, optional): SDK blob data - """ - rendered_content: str - sdk_blob: Optional[bytes] = None - -class Segment: - """ - Content segment. - - Attributes: - part_index (int): Part index in content - start_index (int): Start character index - end_index (int): End character index - text (str, optional): Segment text - """ - part_index: int - start_index: int - end_index: int - text: Optional[str] = None - -class LogprobsResult: - """ - Log probabilities result. - - Attributes: - top_candidates (list[LogprobsResultTopCandidates]): Top candidates per token - chosen_candidates (list[LogprobsResultCandidate]): Chosen candidates - """ - top_candidates: list[LogprobsResultTopCandidates] - chosen_candidates: list[LogprobsResultCandidate] - -class LogprobsResultTopCandidates: - """ - Top candidate tokens and probabilities. - - Attributes: - candidates (list[LogprobsResultCandidate]): Candidate tokens - """ - candidates: list[LogprobsResultCandidate] - -class LogprobsResultCandidate: - """ - Individual token candidate. - - Attributes: - token (str): Token string - token_id (int): Token ID - log_probability (float): Log probability - """ - token: str - token_id: int - log_probability: float - -class GoogleTypeDate: - """ - Date representation. - - Attributes: - year (int): Year - month (int): Month (1-12) - day (int): Day (1-31) - """ - year: int - month: int - day: int -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md b/.tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md deleted file mode 100644 index 58b34bad8..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/embeddings.md +++ /dev/null @@ -1,346 +0,0 @@ -# Embeddings - -Generate text embeddings for semantic search, clustering, similarity comparisons, and other natural language understanding tasks. Embeddings convert text into high-dimensional numerical vectors that capture semantic meaning, enabling mathematical operations on text. - -## Capabilities - -### Embed Content - -Generate embeddings for text content. Supports single or multiple content inputs and various embedding tasks including retrieval, classification, and semantic similarity. - -```python { .api } -def embed_content( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[EmbedContentConfig] = None -) -> EmbedContentResponse: - """ - Generate embeddings for content. - - Parameters: - model (str): Model identifier for embeddings (e.g., 'text-embedding-004', - 'text-multilingual-embedding-002'). Use models optimized for embeddings. - contents (Union[str, list[Content], Content]): Content to embed. Can be: - - str: Simple text to embed - - Content: Content object with text parts - - list[Content]: Multiple content objects to embed in batch - config (EmbedContentConfig, optional): Embedding configuration including: - - task_type: Type of embedding task (retrieval, classification, etc.) - - title: Document title for RETRIEVAL_DOCUMENT task - - output_dimensionality: Desired embedding dimension (model-dependent) - - Returns: - EmbedContentResponse: Response containing embeddings and metadata. - Each embedding is a list of float values representing the vector. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def embed_content( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[EmbedContentConfig] = None -) -> EmbedContentResponse: - """Async version of embed_content.""" - ... -``` - -**Usage Example - Simple Embedding:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Generate embedding for a single text -response = client.models.embed_content( - model='text-embedding-004', - contents='What is the capital of France?' -) - -# Access the embedding vector -embedding = response.embeddings[0].values -print(f"Embedding dimension: {len(embedding)}") -print(f"First 5 values: {embedding[:5]}") -``` - -**Usage Example - Batch Embeddings:** - -```python -from google.genai import Client -from google.genai.types import Content, Part - -client = Client(api_key='YOUR_API_KEY') - -# Embed multiple texts at once -texts = [ - 'Machine learning is a subset of AI', - 'Deep learning uses neural networks', - 'Natural language processing handles text' -] - -contents = [Content(parts=[Part(text=text)]) for text in texts] - -response = client.models.embed_content( - model='text-embedding-004', - contents=contents -) - -print(f"Generated {len(response.embeddings)} embeddings") -for i, embedding in enumerate(response.embeddings): - print(f"Text {i+1}: dimension={len(embedding.values)}") -``` - -**Usage Example - Semantic Similarity:** - -```python -import numpy as np -from google.genai import Client - -def cosine_similarity(vec1, vec2): - """Calculate cosine similarity between two vectors.""" - dot_product = np.dot(vec1, vec2) - norm1 = np.linalg.norm(vec1) - norm2 = np.linalg.norm(vec2) - return dot_product / (norm1 * norm2) - -client = Client(api_key='YOUR_API_KEY') - -# Embed query and documents -query = "machine learning algorithms" -documents = [ - "Neural networks are a type of machine learning model", - "The weather today is sunny and warm", - "Supervised learning requires labeled training data" -] - -# Get embeddings -query_response = client.models.embed_content( - model='text-embedding-004', - contents=query -) -query_embedding = query_response.embeddings[0].values - -doc_embeddings = [] -for doc in documents: - response = client.models.embed_content( - model='text-embedding-004', - contents=doc - ) - doc_embeddings.append(response.embeddings[0].values) - -# Calculate similarities -for i, doc in enumerate(documents): - similarity = cosine_similarity(query_embedding, doc_embeddings[i]) - print(f"Doc {i+1} similarity: {similarity:.4f}") - print(f" {doc}\n") -``` - -**Usage Example - Task-Specific Embeddings:** - -```python -from google.genai import Client -from google.genai.types import EmbedContentConfig, TaskType - -client = Client(api_key='YOUR_API_KEY') - -# Embed for retrieval query -query_config = EmbedContentConfig( - task_type=TaskType.RETRIEVAL_QUERY -) - -query_response = client.models.embed_content( - model='text-embedding-004', - contents='How does photosynthesis work?', - config=query_config -) - -# Embed documents for retrieval -doc_config = EmbedContentConfig( - task_type=TaskType.RETRIEVAL_DOCUMENT, - title='Biology Textbook Chapter 3' -) - -doc_response = client.models.embed_content( - model='text-embedding-004', - contents='Photosynthesis is the process by which plants convert light energy into chemical energy...', - config=doc_config -) - -print("Query embedding generated") -print("Document embedding generated") -``` - -**Usage Example - Reduced Dimensionality:** - -```python -from google.genai import Client -from google.genai.types import EmbedContentConfig - -client = Client(api_key='YOUR_API_KEY') - -# Generate embeddings with reduced dimensionality for efficiency -config = EmbedContentConfig( - output_dimensionality=256 # Reduce from default dimension -) - -response = client.models.embed_content( - model='text-embedding-004', - contents='Text to embed with reduced dimensions', - config=config -) - -embedding = response.embeddings[0].values -print(f"Embedding dimension: {len(embedding)}") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, TypedDict -from enum import Enum - -# Configuration types -class EmbedContentConfig: - """ - Configuration for embedding generation. - - Attributes: - task_type (TaskType, optional): Type of embedding task. Different tasks may - produce optimized embeddings for specific use cases: - - RETRIEVAL_QUERY: Optimize for search queries - - RETRIEVAL_DOCUMENT: Optimize for searchable documents - - SEMANTIC_SIMILARITY: Optimize for similarity comparisons - - CLASSIFICATION: Optimize for text classification - - CLUSTERING: Optimize for clustering tasks - - QUESTION_ANSWERING: Optimize for QA tasks - - FACT_VERIFICATION: Optimize for fact checking - title (str, optional): Document title, used with RETRIEVAL_DOCUMENT task - to provide context for the embedding. - output_dimensionality (int, optional): Desired output dimension for the embedding. - Some models support dimensionality reduction. Smaller dimensions can reduce - storage and computation costs. Check model documentation for supported values. - """ - task_type: Optional[TaskType] = None - title: Optional[str] = None - output_dimensionality: Optional[int] = None - -class TaskType(Enum): - """Embedding task types for optimization.""" - TASK_TYPE_UNSPECIFIED = 'TASK_TYPE_UNSPECIFIED' - RETRIEVAL_QUERY = 'RETRIEVAL_QUERY' - RETRIEVAL_DOCUMENT = 'RETRIEVAL_DOCUMENT' - SEMANTIC_SIMILARITY = 'SEMANTIC_SIMILARITY' - CLASSIFICATION = 'CLASSIFICATION' - CLUSTERING = 'CLUSTERING' - QUESTION_ANSWERING = 'QUESTION_ANSWERING' - FACT_VERIFICATION = 'FACT_VERIFICATION' - CODE_RETRIEVAL_QUERY = 'CODE_RETRIEVAL_QUERY' - -# Response types -class EmbedContentResponse: - """ - Response from embedding generation. - - Attributes: - embeddings (list[ContentEmbedding]): List of embeddings, one for each input content. - Each embedding contains the vector values and optional statistics. - metadata (EmbedContentMetadata, optional): Metadata about the embedding operation. - """ - embeddings: list[ContentEmbedding] - metadata: Optional[EmbedContentMetadata] = None - -class ContentEmbedding: - """ - Individual content embedding. - - Attributes: - values (list[float]): Embedding vector as a list of float values. The length - depends on the model and optional output_dimensionality configuration. - Typical dimensions: 768, 1024, 1536, or custom reduced dimensions. - statistics (ContentEmbeddingStatistics, optional): Statistics about the embedding. - """ - values: list[float] - statistics: Optional[ContentEmbeddingStatistics] = None - -class ContentEmbeddingStatistics: - """ - Statistics about an embedding. - - Attributes: - token_count (int, optional): Number of tokens in the input content. - truncated (bool, optional): Whether the input was truncated to fit model limits. - """ - token_count: Optional[int] = None - truncated: Optional[bool] = None - -class EmbedContentMetadata: - """ - Metadata about the embedding operation. - - Attributes: - model_version (str, optional): Version of the model used for embedding. - """ - model_version: Optional[str] = None - -# Content types (shared with other capabilities) -class Content: - """ - Container for content with role and parts. - - Attributes: - parts (list[Part]): List of content parts - role (str, optional): Role ('user' or 'model') - """ - parts: list[Part] - role: Optional[str] = None - -class Part: - """ - Individual content part. - - For embeddings, typically only text parts are used. - - Attributes: - text (str, optional): Text content to embed - inline_data (Blob, optional): Inline binary data (rarely used for embeddings) - file_data (FileData, optional): Reference to file (rarely used for embeddings) - """ - text: Optional[str] = None - inline_data: Optional[Blob] = None - file_data: Optional[FileData] = None - -class Blob: - """ - Binary data with MIME type. - - Attributes: - mime_type (str): MIME type - data (bytes): Binary data - """ - mime_type: str - data: bytes - -class FileData: - """ - Reference to uploaded file. - - Attributes: - file_uri (str): URI of uploaded file - mime_type (str): MIME type - """ - file_uri: str - mime_type: str - -# TypedDict variants for flexible usage -class EmbedContentConfigDict(TypedDict, total=False): - """TypedDict variant of EmbedContentConfig.""" - task_type: TaskType - title: str - output_dimensionality: int -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md b/.tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md deleted file mode 100644 index 0e81161f8..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/file-search-stores.md +++ /dev/null @@ -1,520 +0,0 @@ -# File Search Stores and Documents - -Create and manage file search stores with document retrieval for retrieval-augmented generation (RAG). File search stores enable semantic search over your documents, allowing models to ground responses in your own data sources. - -## Capabilities - -### Create File Search Store - -Create a file search store to organize and search documents. - -```python { .api } -class FileSearchStores: - """Synchronous file search stores API.""" - - def create( - self, - *, - config: CreateFileSearchStoreConfig - ) -> FileSearchStore: - """ - Create a file search store. - - Parameters: - config (CreateFileSearchStoreConfig): Store configuration including: - - display_name: Display name for the store - - description: Description of the store - - Returns: - FileSearchStore: Created store with name and metadata. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - - @property - def documents(self) -> Documents: - """Access documents sub-API for managing documents within stores.""" - ... - -class AsyncFileSearchStores: - """Asynchronous file search stores API.""" - - async def create( - self, - *, - config: CreateFileSearchStoreConfig - ) -> FileSearchStore: - """Async version of create.""" - ... - - @property - def documents(self) -> AsyncDocuments: - """Access async documents sub-API.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import CreateFileSearchStoreConfig - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -config = CreateFileSearchStoreConfig( - display_name='Product Documentation', - description='Store for product manuals and guides' -) - -store = client.file_search_stores.create(config=config) -print(f"Created store: {store.name}") -``` - -### Get File Search Store - -Retrieve information about a file search store. - -```python { .api } -class FileSearchStores: - """Synchronous file search stores API.""" - - def get(self, *, name: str) -> FileSearchStore: - """ - Get file search store information. - - Parameters: - name (str): Store name in format 'fileSearchStores/*'. - - Returns: - FileSearchStore: Store information. - """ - ... - -class AsyncFileSearchStores: - """Asynchronous file search stores API.""" - - async def get(self, *, name: str) -> FileSearchStore: - """Async version of get.""" - ... -``` - -### Delete File Search Store - -Delete a file search store and all its documents. - -```python { .api } -class FileSearchStores: - """Synchronous file search stores API.""" - - def delete(self, *, name: str) -> None: - """ - Delete a file search store. - - Parameters: - name (str): Store name in format 'fileSearchStores/*'. - """ - ... - -class AsyncFileSearchStores: - """Asynchronous file search stores API.""" - - async def delete(self, *, name: str) -> None: - """Async version of delete.""" - ... -``` - -### Import File - -Import a file into a file search store from GCS. - -```python { .api } -class FileSearchStores: - """Synchronous file search stores API.""" - - def import_file( - self, - *, - store: str, - source: ImportFileSource, - config: Optional[ImportFileConfig] = None - ) -> ImportFileOperation: - """ - Import file into store (returns long-running operation). - - Parameters: - store (str): Store name. - source (ImportFileSource): Import source (GCS URI). - config (ImportFileConfig, optional): Import configuration. - - Returns: - ImportFileOperation: Long-running operation for import. - """ - ... - -class AsyncFileSearchStores: - """Asynchronous file search stores API.""" - - async def import_file( - self, - *, - store: str, - source: ImportFileSource, - config: Optional[ImportFileConfig] = None - ) -> ImportFileOperation: - """Async version of import_file.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import ImportFileSource - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -source = ImportFileSource( - gcs_uri='gs://my-bucket/document.pdf' -) - -operation = client.file_search_stores.import_file( - store='fileSearchStores/abc123', - source=source -) - -# Poll for completion -while not operation.done: - import time - time.sleep(5) - operation = client.operations.get(operation) - -print("Import complete") -``` - -### Upload to File Search Store - -Upload a local file directly to a file search store. - -```python { .api } -class FileSearchStores: - """Synchronous file search stores API.""" - - def upload_to_file_search_store( - self, - *, - store: str, - file: Union[str, Path, IO], - config: Optional[UploadToFileSearchStoreConfig] = None - ) -> UploadToFileSearchStoreOperation: - """ - Upload file to store (returns long-running operation). - - Parameters: - store (str): Store name. - file (Union[str, Path, IO]): File to upload. - config (UploadToFileSearchStoreConfig, optional): Upload configuration including: - - mime_type: File MIME type - - display_name: Display name - - Returns: - UploadToFileSearchStoreOperation: Long-running operation for upload. - """ - ... - -class AsyncFileSearchStores: - """Asynchronous file search stores API.""" - - async def upload_to_file_search_store( - self, - *, - store: str, - file: Union[str, Path, IO], - config: Optional[UploadToFileSearchStoreConfig] = None - ) -> UploadToFileSearchStoreOperation: - """Async version of upload_to_file_search_store.""" - ... -``` - -### List File Search Stores - -List all file search stores. - -```python { .api } -class FileSearchStores: - """Synchronous file search stores API.""" - - def list( - self, - *, - config: Optional[ListFileSearchStoresConfig] = None - ) -> Union[Pager[FileSearchStore], Iterator[FileSearchStore]]: - """ - List file search stores. - - Parameters: - config (ListFileSearchStoresConfig, optional): List configuration. - - Returns: - Union[Pager[FileSearchStore], Iterator[FileSearchStore]]: Paginated store list. - """ - ... - -class AsyncFileSearchStores: - """Asynchronous file search stores API.""" - - async def list( - self, - *, - config: Optional[ListFileSearchStoresConfig] = None - ) -> Union[AsyncPager[FileSearchStore], AsyncIterator[FileSearchStore]]: - """Async version of list.""" - ... -``` - -### Documents Sub-API - -Manage documents within file search stores. - -```python { .api } -class Documents: - """Synchronous documents API.""" - - def get(self, *, name: str) -> Document: - """ - Get document information. - - Parameters: - name (str): Document name in format 'fileSearchStores/*/documents/*'. - - Returns: - Document: Document information. - """ - ... - - def delete(self, *, name: str) -> None: - """ - Delete a document. - - Parameters: - name (str): Document name. - """ - ... - - def list( - self, - *, - parent: str, - config: Optional[ListDocumentsConfig] = None - ) -> Union[Pager[Document], Iterator[Document]]: - """ - List documents in a store. - - Parameters: - parent (str): Store name in format 'fileSearchStores/*'. - config (ListDocumentsConfig, optional): List configuration. - - Returns: - Union[Pager[Document], Iterator[Document]]: Paginated document list. - """ - ... - -class AsyncDocuments: - """Asynchronous documents API.""" - - async def get(self, *, name: str) -> Document: - """Async version of get.""" - ... - - async def delete(self, *, name: str) -> None: - """Async version of delete.""" - ... - - async def list( - self, - *, - parent: str, - config: Optional[ListDocumentsConfig] = None - ) -> Union[AsyncPager[Document], AsyncIterator[Document]]: - """Async version of list.""" - ... -``` - -**Usage Example - Using File Search in RAG:** - -```python -from google.genai import Client -from google.genai.types import ( - GenerateContentConfig, - Tool, - FileSearch -) - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Configure file search tool -config = GenerateContentConfig( - tools=[Tool(file_search=FileSearch( - file_search_store='fileSearchStores/abc123' - ))] -) - -# Generate with grounding in documents -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='What is the return policy for damaged items?', - config=config -) - -print(response.text) - -# Check grounding metadata -if response.candidates[0].grounding_metadata: - print("Grounded in store documents") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, Iterator, AsyncIterator, IO, TypedDict -from pathlib import Path -from datetime import datetime -from enum import Enum - -# Configuration types -class CreateFileSearchStoreConfig: - """ - Configuration for creating file search store. - - Attributes: - display_name (str, optional): Display name. - description (str, optional): Description. - """ - display_name: Optional[str] = None - description: Optional[str] = None - -class ImportFileConfig: - """Configuration for importing files.""" - display_name: Optional[str] = None - -class UploadToFileSearchStoreConfig: - """ - Configuration for uploading files. - - Attributes: - mime_type (str, optional): MIME type. - display_name (str, optional): Display name. - """ - mime_type: Optional[str] = None - display_name: Optional[str] = None - -class ListFileSearchStoresConfig: - """Configuration for listing stores.""" - page_size: Optional[int] = None - page_token: Optional[str] = None - -class ListDocumentsConfig: - """Configuration for listing documents.""" - page_size: Optional[int] = None - page_token: Optional[str] = None - -# Response types -class FileSearchStore: - """ - File search store information. - - Attributes: - name (str): Store resource name. - display_name (str, optional): Display name. - description (str, optional): Description. - create_time (datetime): Creation time. - update_time (datetime): Last update time. - """ - name: str - display_name: Optional[str] = None - description: Optional[str] = None - create_time: datetime - update_time: datetime - -class Document: - """ - Document in file search store. - - Attributes: - name (str): Document resource name. - display_name (str, optional): Display name. - mime_type (str): MIME type. - size_bytes (int): Size in bytes. - state (DocumentState): Processing state. - create_time (datetime): Creation time. - update_time (datetime): Last update time. - """ - name: str - display_name: Optional[str] = None - mime_type: str - size_bytes: int - state: DocumentState - create_time: datetime - update_time: datetime - -class DocumentState(Enum): - """Document processing states.""" - STATE_UNSPECIFIED = 'STATE_UNSPECIFIED' - PROCESSING = 'PROCESSING' - ACTIVE = 'ACTIVE' - FAILED = 'FAILED' - -class ImportFileSource: - """ - Import source. - - Attributes: - gcs_uri (str): GCS URI of file to import. - """ - gcs_uri: str - -# Operation types -class ImportFileOperation: - """Long-running operation for file import.""" - name: str - done: bool - error: Optional[OperationError] = None - response: Optional[dict] = None - -class UploadToFileSearchStoreOperation: - """Long-running operation for file upload.""" - name: str - done: bool - error: Optional[OperationError] = None - response: Optional[dict] = None - -class OperationError: - """Operation error.""" - code: int - message: str - -# Tool types for RAG -class FileSearch: - """ - File search tool configuration. - - Attributes: - file_search_store (str, optional): Store name to search. - """ - file_search_store: Optional[str] = None - -# Pager types -class Pager[T]: - """Synchronous pager.""" - page: list[T] - def next_page(self) -> None: ... - def __iter__(self) -> Iterator[T]: ... - -class AsyncPager[T]: - """Asynchronous pager.""" - page: list[T] - async def next_page(self) -> None: ... - async def __aiter__(self) -> AsyncIterator[T]: ... -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/files.md b/.tessl/tiles/tessl/pypi-google-genai/docs/files.md deleted file mode 100644 index 5298d9ebc..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/files.md +++ /dev/null @@ -1,450 +0,0 @@ -# File Management - -Upload, manage, and download files for use with multimodal content generation. File management is available in the Gemini Developer API only. Files can be images, audio, video, or documents that are referenced in content generation requests. - -## Capabilities - -### Upload File - -Upload a file to be used in content generation. Files are stored temporarily and automatically deleted after a retention period. - -```python { .api } -class Files: - """Synchronous file management API.""" - - def upload( - self, - *, - file: Union[str, Path, IO], - config: Optional[UploadFileConfig] = None - ) -> File: - """ - Upload a file for use in generation requests. - - Parameters: - file (Union[str, Path, IO]): File to upload. Can be: - - str or Path: File path to upload - - IO: File-like object (must be opened in binary mode) - config (UploadFileConfig, optional): Upload configuration including: - - mime_type: MIME type of the file (auto-detected if not provided) - - display_name: Display name for the file - - Returns: - File: Uploaded file information including URI, name, and metadata. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncFiles: - """Asynchronous file management API.""" - - async def upload( - self, - *, - file: Union[str, Path, IO], - config: Optional[UploadFileConfig] = None - ) -> File: - """Async version of upload.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import UploadFileConfig - -client = Client(api_key='YOUR_API_KEY') - -# Upload from file path -config = UploadFileConfig( - mime_type='image/jpeg', - display_name='Product Photo' -) - -file = client.files.upload( - file='product.jpg', - config=config -) - -print(f"Uploaded file: {file.name}") -print(f"URI: {file.uri}") -print(f"State: {file.state}") - -# Use in generation -from google.genai.types import Content, Part, FileData - -content = Content( - parts=[ - Part(text='Describe this image'), - Part(file_data=FileData(file_uri=file.uri, mime_type=file.mime_type)) - ] -) - -response = client.models.generate_content( - model='gemini-2.0-flash', - contents=content -) -print(response.text) -``` - -### Get File - -Retrieve information about an uploaded file. - -```python { .api } -class Files: - """Synchronous file management API.""" - - def get(self, *, name: str) -> File: - """ - Get file information. - - Parameters: - name (str): File name (from File.name) in format 'files/{file_id}'. - - Returns: - File: File information including state, size, and metadata. - - Raises: - ClientError: For client errors including 404 if file not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncFiles: - """Asynchronous file management API.""" - - async def get(self, *, name: str) -> File: - """Async version of get.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Get file info -file = client.files.get(name='files/abc123') - -print(f"Name: {file.display_name}") -print(f"Size: {file.size_bytes} bytes") -print(f"State: {file.state}") -print(f"Created: {file.create_time}") -print(f"Expires: {file.expiration_time}") -``` - -### Delete File - -Delete an uploaded file. Files are automatically deleted after their expiration time, but you can delete them earlier if needed. - -```python { .api } -class Files: - """Synchronous file management API.""" - - def delete(self, *, name: str) -> None: - """ - Delete a file. - - Parameters: - name (str): File name in format 'files/{file_id}'. - - Raises: - ClientError: For client errors including 404 if file not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncFiles: - """Asynchronous file management API.""" - - async def delete(self, *, name: str) -> None: - """Async version of delete.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Delete file -client.files.delete(name='files/abc123') -print("File deleted") -``` - -### Download File - -Download the contents of an uploaded file. - -```python { .api } -class Files: - """Synchronous file management API.""" - - def download(self, *, name: str, path: Optional[str] = None) -> bytes: - """ - Download file contents. - - Parameters: - name (str): File name in format 'files/{file_id}'. - path (str, optional): Local file path to save to. If not provided, - returns file contents as bytes without saving. - - Returns: - bytes: File contents. If path is provided, also saves to file. - - Raises: - ClientError: For client errors including 404 if file not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncFiles: - """Asynchronous file management API.""" - - async def download(self, *, name: str, path: Optional[str] = None) -> bytes: - """Async version of download.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Download to file -data = client.files.download(name='files/abc123', path='downloaded.jpg') -print(f"Downloaded {len(data)} bytes") - -# Download to memory -data = client.files.download(name='files/abc123') -# Process data in memory -``` - -### List Files - -List all uploaded files with optional pagination. - -```python { .api } -class Files: - """Synchronous file management API.""" - - def list( - self, - *, - config: Optional[ListFilesConfig] = None - ) -> Union[Pager[File], Iterator[File]]: - """ - List uploaded files. - - Parameters: - config (ListFilesConfig, optional): List configuration including: - - page_size: Number of files per page (default: 50, max: 100) - - page_token: Token for pagination - - Returns: - Union[Pager[File], Iterator[File]]: Paginated file list. If page_size - is set, returns Pager for manual pagination. Otherwise, returns - Iterator that automatically handles pagination. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncFiles: - """Asynchronous file management API.""" - - async def list( - self, - *, - config: Optional[ListFilesConfig] = None - ) -> Union[AsyncPager[File], AsyncIterator[File]]: - """Async version of list.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# List all files (auto-pagination) -for file in client.files.list(): - print(f"{file.display_name}: {file.name} ({file.state})") - -# Manual pagination -from google.genai.types import ListFilesConfig - -config = ListFilesConfig(page_size=10) -pager = client.files.list(config=config) - -print(f"Page 1: {len(pager.page)} files") -for file in pager.page: - print(f" {file.display_name}") - -# Get next page -pager.next_page() -print(f"Page 2: {len(pager.page)} files") -``` - -## Types - -```python { .api } -from typing import Optional, Union, IO, Iterator, AsyncIterator -from pathlib import Path -from enum import Enum -from datetime import datetime - -# Configuration types -class UploadFileConfig: - """ - Configuration for file upload. - - Attributes: - mime_type (str, optional): MIME type of the file. If not provided, will be - auto-detected from file extension or content. - display_name (str, optional): Human-readable display name for the file. - """ - mime_type: Optional[str] = None - display_name: Optional[str] = None - -class ListFilesConfig: - """ - Configuration for listing files. - - Attributes: - page_size (int, optional): Number of files per page (1-100). Default: 50. - page_token (str, optional): Token from previous response for pagination. - """ - page_size: Optional[int] = None - page_token: Optional[str] = None - -# Response types -class File: - """ - Uploaded file information. - - Attributes: - name (str): File resource name in format 'files/{file_id}'. - display_name (str, optional): Display name for the file. - mime_type (str): MIME type of the file. - size_bytes (int): File size in bytes. - create_time (datetime): When file was created. - update_time (datetime): When file was last updated. - expiration_time (datetime): When file will be automatically deleted. - sha256_hash (bytes): SHA256 hash of file contents. - uri (str): URI for referencing the file in API requests. - state (FileState): Current processing state of the file. - error (FileError, optional): Error if file processing failed. - """ - name: str - display_name: Optional[str] = None - mime_type: str - size_bytes: int - create_time: datetime - update_time: datetime - expiration_time: datetime - sha256_hash: bytes - uri: str - state: FileState - error: Optional[FileError] = None - -class FileState(Enum): - """File processing state.""" - STATE_UNSPECIFIED = 'STATE_UNSPECIFIED' - PROCESSING = 'PROCESSING' - ACTIVE = 'ACTIVE' - FAILED = 'FAILED' - -class FileError: - """ - File processing error. - - Attributes: - code (int): Error code. - message (str): Error message. - """ - code: int - message: str - -class FileData: - """ - Reference to uploaded file for use in content. - - Attributes: - file_uri (str): URI from File.uri. - mime_type (str): MIME type from File.mime_type. - """ - file_uri: str - mime_type: str - -# Pager types -class Pager[T]: - """ - Synchronous pager for paginated results. - - Attributes: - page (list[T]): Current page of items. - page_size (int): Number of items per page. - - Methods: - next_page(): Fetch next page of results. - """ - page: list[T] - page_size: int - - def next_page(self) -> None: - """Fetch next page and update self.page.""" - ... - - def __iter__(self) -> Iterator[T]: - """Iterate over all items across pages.""" - ... - -class AsyncPager[T]: - """ - Asynchronous pager for paginated results. - - Attributes: - page (list[T]): Current page of items. - page_size (int): Number of items per page. - - Methods: - next_page(): Fetch next page of results. - """ - page: list[T] - page_size: int - - async def next_page(self) -> None: - """Fetch next page and update self.page.""" - ... - - async def __aiter__(self) -> AsyncIterator[T]: - """Async iterate over all items across pages.""" - ... - -# TypedDict variants -class UploadFileConfigDict(TypedDict, total=False): - """TypedDict variant of UploadFileConfig.""" - mime_type: str - display_name: str - -class ListFilesConfigDict(TypedDict, total=False): - """TypedDict variant of ListFilesConfig.""" - page_size: int - page_token: str -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md b/.tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md deleted file mode 100644 index ce9e86ea3..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/image-generation.md +++ /dev/null @@ -1,551 +0,0 @@ -# Image Generation - -Generate, edit, upscale, and segment images using Imagen models. Provides comprehensive image generation and manipulation capabilities including text-to-image generation, image editing with masks, upscaling for enhanced resolution, image recontextualization, and semantic segmentation. - -## Capabilities - -### Generate Images - -Generate images from text prompts using Imagen models. Supports various aspect ratios, safety filtering, and configuration options. - -```python { .api } -def generate_images( - *, - model: str, - prompt: str, - config: Optional[GenerateImagesConfig] = None -) -> GenerateImagesResponse: - """ - Generate images from a text prompt. - - Parameters: - model (str): Model identifier (e.g., 'imagen-3.0-generate-001', 'imagen-3.0-fast-generate-001'). - prompt (str): Text description of the image to generate. More detailed prompts - typically produce better results. - config (GenerateImagesConfig, optional): Generation configuration including: - - number_of_images: Number of images to generate (1-8) - - aspect_ratio: Image aspect ratio ('1:1', '16:9', '9:16', '4:3', '3:4') - - negative_prompt: What to avoid in the image - - safety_filter_level: Safety filtering level - - person_generation: Control person generation in images - - language: Language of the prompt - - Returns: - GenerateImagesResponse: Response containing generated images and metadata. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def generate_images( - *, - model: str, - prompt: str, - config: Optional[GenerateImagesConfig] = None -) -> GenerateImagesResponse: - """Async version of generate_images.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import GenerateImagesConfig - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -config = GenerateImagesConfig( - number_of_images=4, - aspect_ratio='16:9', - negative_prompt='blurry, low quality', - safety_filter_level='block_some' -) - -response = client.models.generate_images( - model='imagen-3.0-generate-001', - prompt='A serene mountain landscape at sunset with a lake', - config=config -) - -for i, image in enumerate(response.generated_images): - image.pil_image.save(f'generated_{i}.png') -``` - -### Edit Image - -Edit existing images using text prompts and optional masks to specify editing regions. - -```python { .api } -def edit_image( - *, - model: str, - prompt: str, - reference_images: Sequence[ReferenceImage], - config: Optional[EditImageConfig] = None -) -> EditImageResponse: - """ - Edit an image using a text prompt and reference images. - - Parameters: - model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). - prompt (str): Text description of desired edits. - reference_images (Sequence[ReferenceImage]): Reference images for editing. - Can include base image, mask image, and control images. - config (EditImageConfig, optional): Editing configuration including: - - number_of_images: Number of edited variations - - negative_prompt: What to avoid - - edit_mode: Editing mode (INPAINT, OUTPAINT, etc.) - - mask_mode: Mask interpretation mode - - mask_dilation: Mask dilation amount - - guidance_scale: Prompt adherence strength - - safety_filter_level: Safety filtering - - Returns: - EditImageResponse: Response containing edited images. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def edit_image( - *, - model: str, - prompt: str, - reference_images: Sequence[ReferenceImage], - config: Optional[EditImageConfig] = None -) -> EditImageResponse: - """Async version of edit_image.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import ( - EditImageConfig, - ReferenceImage, - Image, - EditMode -) - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Load images -base_image = Image.from_file('photo.jpg') -mask_image = Image.from_file('mask.png') # White areas will be edited - -reference_images = [ - ReferenceImage(reference_type='reference_image', reference_image=base_image), - ReferenceImage(reference_type='mask_image', reference_image=mask_image) -] - -config = EditImageConfig( - number_of_images=2, - edit_mode=EditMode.INPAINT_INSERTION, - negative_prompt='distorted, blurry' -) - -response = client.models.edit_image( - model='imagen-3.0-capability-001', - prompt='A red sports car', - reference_images=reference_images, - config=config -) - -for i, image in enumerate(response.generated_images): - image.pil_image.save(f'edited_{i}.png') -``` - -### Upscale Image - -Upscale images to higher resolutions while preserving quality and details. - -```python { .api } -def upscale_image( - *, - model: str, - image: Image, - upscale_factor: str, - config: Optional[UpscaleImageConfig] = None -) -> UpscaleImageResponse: - """ - Upscale an image to higher resolution. - - Parameters: - model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). - image (Image): Image to upscale. - upscale_factor (str): Upscaling factor ('x2' or 'x4'). - config (UpscaleImageConfig, optional): Upscaling configuration including: - - number_of_images: Number of upscaled variations - - safety_filter_level: Safety filtering level - - Returns: - UpscaleImageResponse: Response containing upscaled images. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def upscale_image( - *, - model: str, - image: Image, - upscale_factor: str, - config: Optional[UpscaleImageConfig] = None -) -> UpscaleImageResponse: - """Async version of upscale_image.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import Image, UpscaleImageConfig - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -image = Image.from_file('low_res.jpg') - -config = UpscaleImageConfig( - number_of_images=1 -) - -response = client.models.upscale_image( - model='imagen-3.0-capability-001', - image=image, - upscale_factor='x4', - config=config -) - -response.generated_images[0].pil_image.save('upscaled_4x.png') -``` - -### Recontext Image - -Recontextualize product images by changing their backgrounds or settings while preserving the main subject. - -```python { .api } -def recontext_image( - *, - model: str, - prompt: str, - source: RecontextImageSource, - config: Optional[RecontextImageConfig] = None -) -> RecontextImageResponse: - """ - Recontextualize a product image with a new background. - - Parameters: - model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). - prompt (str): Description of the new context/background. - source (RecontextImageSource): Source containing product image and optional mask. - config (RecontextImageConfig, optional): Configuration including: - - number_of_images: Number of variations - - negative_prompt: What to avoid - - safety_filter_level: Safety filtering - - Returns: - RecontextImageResponse: Response with recontextualized images. - """ - ... - -async def recontext_image( - *, - model: str, - prompt: str, - source: RecontextImageSource, - config: Optional[RecontextImageConfig] = None -) -> RecontextImageResponse: - """Async version of recontext_image.""" - ... -``` - -### Segment Image - -Perform semantic segmentation on images to identify and isolate different objects and regions. - -```python { .api } -def segment_image( - *, - model: str, - source: SegmentImageSource, - config: Optional[SegmentImageConfig] = None -) -> SegmentImageResponse: - """ - Segment an image to identify objects and regions. - - Parameters: - model (str): Model identifier (e.g., 'imagen-3.0-capability-001'). - source (SegmentImageSource): Source image and optional entity labels. - config (SegmentImageConfig, optional): Segmentation configuration including: - - segment_mode: Segmentation mode (FOREGROUND, ENTITY) - - Returns: - SegmentImageResponse: Response with segmentation masks. - """ - ... - -async def segment_image( - *, - model: str, - source: SegmentImageSource, - config: Optional[SegmentImageConfig] = None -) -> SegmentImageResponse: - """Async version of segment_image.""" - ... -``` - -## Types - -```python { .api } -from typing import Optional, List, Sequence, Literal -from enum import Enum -import PIL.Image - -# Configuration types -class GenerateImagesConfig: - """ - Configuration for image generation. - - Attributes: - number_of_images (int, optional): Number of images to generate (1-8). Default: 1. - aspect_ratio (str, optional): Aspect ratio ('1:1', '16:9', '9:16', '4:3', '3:4'). Default: '1:1'. - negative_prompt (str, optional): What to avoid in generated images. - safety_filter_level (str, optional): Safety filtering ('block_most', 'block_some', 'block_few'). - person_generation (PersonGeneration, optional): Control person generation. - include_safety_attributes (bool, optional): Include safety attributes in response. - language (str, optional): Language of the prompt. - add_watermark (bool, optional): Add watermark to images. - """ - number_of_images: Optional[int] = None - aspect_ratio: Optional[str] = None - negative_prompt: Optional[str] = None - safety_filter_level: Optional[str] = None - person_generation: Optional[PersonGeneration] = None - include_safety_attributes: Optional[bool] = None - language: Optional[str] = None - add_watermark: Optional[bool] = None - -class EditImageConfig: - """ - Configuration for image editing. - - Attributes: - number_of_images (int, optional): Number of edited variations. - negative_prompt (str, optional): What to avoid. - edit_mode (EditMode, optional): Editing mode. - mask_mode (MaskMode, optional): Mask interpretation. - mask_dilation (float, optional): Mask dilation amount. - guidance_scale (float, optional): Prompt adherence (1-20). - safety_filter_level (str, optional): Safety filtering. - person_generation (PersonGeneration, optional): Person generation control. - include_safety_attributes (bool, optional): Include safety attributes. - """ - number_of_images: Optional[int] = None - negative_prompt: Optional[str] = None - edit_mode: Optional[EditMode] = None - mask_mode: Optional[MaskMode] = None - mask_dilation: Optional[float] = None - guidance_scale: Optional[float] = None - safety_filter_level: Optional[str] = None - person_generation: Optional[PersonGeneration] = None - include_safety_attributes: Optional[bool] = None - -class UpscaleImageConfig: - """ - Configuration for image upscaling. - - Attributes: - number_of_images (int, optional): Number of upscaled variations. - safety_filter_level (str, optional): Safety filtering. - include_safety_attributes (bool, optional): Include safety attributes. - """ - number_of_images: Optional[int] = None - safety_filter_level: Optional[str] = None - include_safety_attributes: Optional[bool] = None - -class RecontextImageConfig: - """Configuration for image recontextualization.""" - number_of_images: Optional[int] = None - negative_prompt: Optional[str] = None - safety_filter_level: Optional[str] = None - include_safety_attributes: Optional[bool] = None - -class SegmentImageConfig: - """ - Configuration for image segmentation. - - Attributes: - segment_mode (SegmentMode, optional): Segmentation mode. - """ - segment_mode: Optional[SegmentMode] = None - -# Response types -class GenerateImagesResponse: - """ - Response from image generation. - - Attributes: - generated_images (list[GeneratedImage]): Generated images with metadata. - rai_filtered_reason (str, optional): Reason if filtered by safety. - """ - generated_images: list[GeneratedImage] - rai_filtered_reason: Optional[str] = None - -class EditImageResponse: - """Response from image editing.""" - generated_images: list[GeneratedImage] - rai_filtered_reason: Optional[str] = None - -class UpscaleImageResponse: - """Response from image upscaling.""" - generated_images: list[GeneratedImage] - rai_filtered_reason: Optional[str] = None - -class RecontextImageResponse: - """Response from image recontextualization.""" - generated_images: list[GeneratedImage] - rai_filtered_reason: Optional[str] = None - -class SegmentImageResponse: - """ - Response from image segmentation. - - Attributes: - generated_masks (list[GeneratedImageMask]): Segmentation masks. - """ - generated_masks: list[GeneratedImageMask] - -class GeneratedImage: - """ - Generated or edited image. - - Attributes: - image (Image): Image object. - pil_image (PIL.Image.Image): PIL Image for easy manipulation. - generation_seed (int, optional): Seed used for generation. - rai_info (str, optional): RAI information. - safety_attributes (SafetyAttributes, optional): Safety attributes. - """ - image: Image - pil_image: PIL.Image.Image - generation_seed: Optional[int] = None - rai_info: Optional[str] = None - safety_attributes: Optional[SafetyAttributes] = None - -class GeneratedImageMask: - """ - Segmentation mask. - - Attributes: - mask_image (Image): Mask image. - entity_label (EntityLabel, optional): Entity label. - """ - mask_image: Image - entity_label: Optional[EntityLabel] = None - -# Input types -class ReferenceImage: - """ - Reference image for editing. - - Attributes: - reference_type (str): Type ('reference_image', 'mask_image', 'control_image', etc.). - reference_image (Image): The reference image. - reference_id (int, optional): Reference identifier. - """ - reference_type: str - reference_image: Image - reference_id: Optional[int] = None - -class RecontextImageSource: - """ - Source for recontextualization. - - Attributes: - product_image (Image): Product image. - product_mask (Image, optional): Product mask. - """ - product_image: Image - product_mask: Optional[Image] = None - -class SegmentImageSource: - """ - Source for segmentation. - - Attributes: - image (Image): Image to segment. - entity_labels (list[EntityLabel], optional): Entity labels to identify. - """ - image: Image - entity_labels: Optional[list[EntityLabel]] = None - -class Image: - """ - Image data supporting multiple formats. - - Can be created from: - - File: Image.from_file('path.jpg') - - URL: Image.from_url('https://...') - - Bytes: Image.from_bytes(data, mime_type='image/jpeg') - - PIL: Image.from_pil(pil_image) - """ - @staticmethod - def from_file(path: str) -> Image: ... - - @staticmethod - def from_url(url: str) -> Image: ... - - @staticmethod - def from_bytes(data: bytes, mime_type: str) -> Image: ... - - @staticmethod - def from_pil(pil_image: PIL.Image.Image) -> Image: ... - -# Enum types -class PersonGeneration(Enum): - """Person generation control.""" - DONT_ALLOW = 'dont_allow' - ALLOW_ADULT = 'allow_adult' - ALLOW_ALL = 'allow_all' - -class EditMode(Enum): - """Image editing modes.""" - EDIT_MODE_UNSPECIFIED = 'EDIT_MODE_UNSPECIFIED' - INPAINT_INSERTION = 'INPAINT_INSERTION' - INPAINT_REMOVAL = 'INPAINT_REMOVAL' - OUTPAINT = 'OUTPAINT' - PRODUCT_IMAGE = 'PRODUCT_IMAGE' - -class MaskMode(Enum): - """Mask interpretation modes.""" - MASK_MODE_UNSPECIFIED = 'MASK_MODE_UNSPECIFIED' - MASK_MODE_BACKGROUND = 'MASK_MODE_BACKGROUND' - MASK_MODE_FOREGROUND = 'MASK_MODE_FOREGROUND' - MASK_MODE_SEMANTIC = 'MASK_MODE_SEMANTIC' - -class SegmentMode(Enum): - """Segmentation modes.""" - SEGMENT_MODE_UNSPECIFIED = 'SEGMENT_MODE_UNSPECIFIED' - FOREGROUND = 'FOREGROUND' - ENTITY = 'ENTITY' - -class SafetyAttributes: - """Safety attributes for images.""" - blocked: bool - scores: dict[str, float] - -class EntityLabel: - """Entity label for segmentation.""" - label: str - score: Optional[float] = None -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/index.md b/.tessl/tiles/tessl/pypi-google-genai/docs/index.md deleted file mode 100644 index 10ab2877a..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/index.md +++ /dev/null @@ -1,599 +0,0 @@ -# Google GenAI Python SDK - -A comprehensive Python SDK for integrating Google's generative AI models into applications, supporting both the Gemini Developer API and Vertex AI APIs. The SDK provides a unified interface for content generation, multi-turn conversations, embeddings, image and video generation, file management, caching, batch processing, fine-tuning, and real-time bidirectional streaming. - -## Package Information - -- **Package Name**: google-genai -- **Language**: Python -- **Installation**: `pip install google-genai` -- **Version**: 1.51.0 - -## Core Imports - -```python -from google import genai -``` - -Common imports: - -```python -from google.genai import Client -from google.genai import types -``` - -## Basic Usage - -```python -from google.genai import Client - -# Initialize client with API key (Gemini Developer API) -client = Client(api_key='YOUR_API_KEY') - -# Generate content -response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Explain how AI works in simple terms.' -) -print(response.text) - -# Multi-turn chat -chat = client.chats.create(model='gemini-2.0-flash') -response = chat.send_message('What is machine learning?') -print(response.text) - -# Close client -client.close() -``` - -With context manager: - -```python -from google.genai import Client - -with Client(api_key='YOUR_API_KEY') as client: - response = client.models.generate_content( - model='gemini-2.0-flash', - contents='Hello!' - ) - print(response.text) -``` - -## Architecture - -The SDK follows a modular architecture with these key components: - -- **Client**: Main entry point providing access to all API modules, supports both Gemini Developer API (api_key) and Vertex AI (credentials/project) -- **API Modules**: Specialized interfaces for models, chats, files, caches, batches, tunings, file_search_stores, operations, live -- **Types System**: Comprehensive type definitions with both Pydantic models and TypedDict variants for flexible usage -- **Async Support**: Complete async/await implementations via the `aio` property on the Client -- **Streaming**: Built-in streaming support for content generation and live interactions -- **Function Calling**: Automatic and manual function calling with Python functions or JSON schemas - -## Capabilities - -### Client Initialization - -Initialize the main client to access all SDK functionality with support for both Gemini Developer API and Vertex AI. - -```python { .api } -class Client: - def __init__( - self, - *, - vertexai: Optional[bool] = None, - api_key: Optional[str] = None, - credentials: Optional[google.auth.credentials.Credentials] = None, - project: Optional[str] = None, - location: Optional[str] = None, - debug_config: Optional[DebugConfig] = None, - http_options: Optional[Union[HttpOptions, HttpOptionsDict]] = None - ): ... - - @property - def aio(self) -> AsyncClient: ... - - def close(self) -> None: ... - def __enter__(self) -> 'Client': ... - def __exit__(self, *args) -> None: ... - -class AsyncClient: - @property - def models(self) -> AsyncModels: ... - @property - def chats(self) -> AsyncChats: ... - @property - def files(self) -> AsyncFiles: ... - @property - def caches(self) -> AsyncCaches: ... - @property - def batches(self) -> AsyncBatches: ... - @property - def tunings(self) -> AsyncTunings: ... - @property - def file_search_stores(self) -> AsyncFileSearchStores: ... - @property - def live(self) -> AsyncLive: ... - @property - def operations(self) -> AsyncOperations: ... - - async def aclose(self) -> None: ... - async def __aenter__(self) -> 'AsyncClient': ... - async def __aexit__(self, *args) -> None: ... -``` - -[Client Initialization](./client.md) - -### Content Generation - -Generate text and multimodal content using Gemini models with support for streaming, function calling, structured output, and extensive configuration options. - -```python { .api } -def generate_content( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[GenerateContentConfig] = None -) -> GenerateContentResponse: ... - -def generate_content_stream( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[GenerateContentConfig] = None -) -> Iterator[GenerateContentResponse]: ... -``` - -[Content Generation](./content-generation.md) - -### Multi-Turn Conversations - -Create and manage chat sessions for multi-turn conversations with automatic history management. - -```python { .api } -class Chat: - def send_message( - self, - message: Union[str, Content], - config: Optional[GenerateContentConfig] = None - ) -> GenerateContentResponse: ... - - def send_message_stream( - self, - message: Union[str, Content], - config: Optional[GenerateContentConfig] = None - ) -> Iterator[GenerateContentResponse]: ... - - def get_history(self, curated: bool = False) -> list[Content]: ... -``` - -[Multi-Turn Conversations](./chats.md) - -### Embeddings - -Generate text embeddings for semantic search, clustering, and similarity comparisons. - -```python { .api } -def embed_content( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[EmbedContentConfig] = None -) -> EmbedContentResponse: ... -``` - -[Embeddings](./embeddings.md) - -### Image Generation - -Generate, edit, upscale, and segment images using Imagen models. - -```python { .api } -def generate_images( - *, - model: str, - prompt: str, - config: Optional[GenerateImagesConfig] = None -) -> GenerateImagesResponse: ... - -def edit_image( - *, - model: str, - prompt: str, - reference_images: Sequence[ReferenceImage], - config: Optional[EditImageConfig] = None -) -> EditImageResponse: ... - -def upscale_image( - *, - model: str, - image: Image, - upscale_factor: str, - config: Optional[UpscaleImageConfig] = None -) -> UpscaleImageResponse: ... -``` - -[Image Generation](./image-generation.md) - -### Video Generation - -Generate videos from prompts, images, or existing videos using Veo models. - -```python { .api } -def generate_videos( - *, - model: str, - prompt: Optional[str] = None, - image: Optional[Image] = None, - video: Optional[Video] = None, - config: Optional[GenerateVideosConfig] = None -) -> GenerateVideosOperation: ... -``` - -[Video Generation](./video-generation.md) - -### File Management - -Upload, manage, and download files for use with multimodal content generation (Gemini Developer API only). - -```python { .api } -class Files: - def upload( - self, - *, - file: Union[str, Path, IO], - config: Optional[UploadFileConfig] = None - ) -> File: ... - - def get(self, *, name: str) -> File: ... - def delete(self, *, name: str) -> None: ... - def download(self, *, name: str, path: Optional[str] = None) -> bytes: ... - def list(self, *, config: Optional[ListFilesConfig] = None) -> Union[Pager[File], Iterator[File]]: ... -``` - -[File Management](./files.md) - -### Context Caching - -Create and manage cached content to reduce costs and latency for repeated requests with shared context. - -```python { .api } -class Caches: - def create( - self, - *, - model: str, - config: CreateCachedContentConfig - ) -> CachedContent: ... - - def get(self, *, name: str) -> CachedContent: ... - def update(self, *, name: str, config: UpdateCachedContentConfig) -> CachedContent: ... - def delete(self, *, name: str) -> None: ... - def list(self, *, config: Optional[ListCachedContentsConfig] = None) -> Union[Pager[CachedContent], Iterator[CachedContent]]: ... -``` - -[Context Caching](./caching.md) - -### Batch Processing - -Submit batch prediction jobs for high-volume inference with cost savings. - -```python { .api } -class Batches: - def create( - self, - *, - model: str, - src: Union[str, list[dict]], - dest: Optional[str] = None, - config: Optional[CreateBatchJobConfig] = None - ) -> BatchJob: ... - - def create_embeddings( - self, - *, - model: str, - src: Union[str, list[dict]], - dest: Optional[str] = None, - config: Optional[CreateBatchJobConfig] = None - ) -> BatchJob: ... - - def get(self, *, name: str) -> BatchJob: ... - def cancel(self, *, name: str) -> None: ... - def delete(self, *, name: str) -> None: ... - def list(self, *, config: Optional[ListBatchJobsConfig] = None) -> Union[Pager[BatchJob], Iterator[BatchJob]]: ... -``` - -[Batch Processing](./batches.md) - -### Model Fine-Tuning - -Create and manage supervised fine-tuning jobs to customize models (Vertex AI only). - -```python { .api } -class Tunings: - def tune( - self, - *, - base_model: str, - training_dataset: TuningDataset, - config: Optional[CreateTuningJobConfig] = None - ) -> TuningJob: ... - - def get(self, *, name: str, config: Optional[GetTuningJobConfig] = None) -> TuningJob: ... - def cancel(self, *, name: str) -> None: ... - def list(self, *, config: Optional[ListTuningJobsConfig] = None) -> Union[Pager[TuningJob], Iterator[TuningJob]]: ... -``` - -[Model Fine-Tuning](./tuning.md) - -### File Search Stores - -Create and manage file search stores with document retrieval for retrieval-augmented generation. - -```python { .api } -class FileSearchStores: - def create(self, *, config: CreateFileSearchStoreConfig) -> FileSearchStore: ... - def get(self, *, name: str) -> FileSearchStore: ... - def delete(self, *, name: str) -> None: ... - def import_file( - self, - *, - store: str, - source: ImportFileSource, - config: Optional[ImportFileConfig] = None - ) -> ImportFileOperation: ... - def upload_to_file_search_store( - self, - *, - store: str, - file: Union[str, Path, IO], - config: Optional[UploadToFileSearchStoreConfig] = None - ) -> UploadToFileSearchStoreOperation: ... - def list(self, *, config: Optional[ListFileSearchStoresConfig] = None) -> Union[Pager[FileSearchStore], Iterator[FileSearchStore]]: ... -``` - -[File Search Stores](./file-search-stores.md) - -### Live API - -Real-time bidirectional streaming for interactive applications with support for audio, video, and function calling (Preview). - -```python { .api } -class AsyncLive: - async def connect( - self, - *, - model: str, - config: Optional[LiveConnectConfig] = None - ) -> AsyncIterator[AsyncSession]: ... - -class AsyncSession: - async def send_client_content( - self, - *, - turns: Optional[Union[Content, list[Content]]] = None, - turn_complete: bool = False - ) -> None: ... - - async def send_realtime_input( - self, - *, - media_chunks: Optional[Sequence[Blob]] = None - ) -> None: ... - - async def send_tool_response( - self, - *, - function_responses: Sequence[FunctionResponse] - ) -> None: ... - - async def receive(self) -> AsyncIterator[LiveServerMessage]: ... - async def close(self) -> None: ... -``` - -[Live API](./live.md) - -### Token Operations - -Count tokens and compute detailed token information for content, with support for local tokenization without API calls. - -```python { .api } -def count_tokens( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[CountTokensConfig] = None -) -> CountTokensResponse: ... - -def compute_tokens( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[ComputeTokensConfig] = None -) -> ComputeTokensResponse: ... - -class LocalTokenizer: - def __init__(self, model_name: str): ... - def count_tokens( - self, - contents: Union[str, list[Content], Content], - *, - config: Optional[CountTokensConfig] = None - ) -> CountTokensResult: ... - def compute_tokens( - self, - contents: Union[str, list[Content], Content] - ) -> ComputeTokensResult: ... -``` - -[Token Operations](./tokens.md) - -### Model Information - -Retrieve and manage model information and capabilities. - -```python { .api } -def get(self, *, model: str, config: Optional[GetModelConfig] = None) -> Model: ... -def update(self, *, model: str, config: UpdateModelConfig) -> Model: ... -def delete(self, *, model: str, config: Optional[DeleteModelConfig] = None) -> DeleteModelResponse: ... -def list(self, *, config: Optional[ListModelsConfig] = None) -> Union[Pager[Model], Iterator[Model]]: ... -``` - -[Model Information](./models.md) - -### Long-Running Operations - -Monitor and retrieve status of long-running operations like video generation and file imports. - -```python { .api } -class Operations: - def get(self, operation: Union[Operation, str]) -> Operation: ... -``` - -[Long-Running Operations](./operations.md) - -## Core Types - -### Content and Parts - -```python { .api } -class Content: - """Container for conversation content with role and parts.""" - parts: list[Part] - role: Optional[str] = None - -class Part: - """Individual content part - text, image, video, function call, etc.""" - text: Optional[str] = None - inline_data: Optional[Blob] = None - file_data: Optional[FileData] = None - function_call: Optional[FunctionCall] = None - function_response: Optional[FunctionResponse] = None - executable_code: Optional[ExecutableCode] = None - code_execution_result: Optional[CodeExecutionResult] = None - -class Blob: - """Binary data with MIME type.""" - mime_type: str - data: bytes - -class Image: - """Image data - can be URL, file path, bytes, PIL Image, or FileData.""" - # Various constructors supported - -class Video: - """Video data - can be URL, file path, bytes, or FileData.""" - # Various constructors supported -``` - -### Generation Configuration - -```python { .api } -class GenerateContentConfig: - """Configuration for content generation.""" - system_instruction: Optional[Union[str, Content]] = None - contents: Optional[Union[str, list[Content], Content]] = None - generation_config: Optional[GenerationConfig] = None - safety_settings: Optional[list[SafetySetting]] = None - tools: Optional[list[Tool]] = None - tool_config: Optional[ToolConfig] = None - cached_content: Optional[str] = None - -class GenerationConfig: - """Core generation parameters.""" - temperature: Optional[float] = None - top_p: Optional[float] = None - top_k: Optional[int] = None - candidate_count: Optional[int] = None - max_output_tokens: Optional[int] = None - stop_sequences: Optional[list[str]] = None - response_mime_type: Optional[str] = None - response_schema: Optional[Schema] = None -``` - -### Response Types - -```python { .api } -class GenerateContentResponse: - """Response from content generation.""" - text: str # Convenience property - candidates: list[Candidate] - usage_metadata: Optional[GenerateContentResponseUsageMetadata] = None - prompt_feedback: Optional[GenerateContentResponsePromptFeedback] = None - -class Candidate: - """Generated candidate with content and metadata.""" - content: Content - finish_reason: Optional[FinishReason] = None - safety_ratings: Optional[list[SafetyRating]] = None - citation_metadata: Optional[CitationMetadata] = None - grounding_metadata: Optional[GroundingMetadata] = None -``` - -### Function Calling - -```python { .api } -class Tool: - """Tool containing function declarations.""" - function_declarations: Optional[list[FunctionDeclaration]] = None - google_search: Optional[GoogleSearch] = None - code_execution: Optional[ToolCodeExecution] = None - -class FunctionDeclaration: - """Function schema definition.""" - name: str - description: str - parameters: Optional[Schema] = None - -class FunctionCall: - """Function invocation from model.""" - name: str - args: dict[str, Any] - -class FunctionResponse: - """Function execution response.""" - name: str - response: dict[str, Any] -``` - -### Error Handling - -```python { .api } -class APIError(Exception): - """Base exception for API errors.""" - code: int - status: Optional[str] - message: Optional[str] - details: Any - -class ClientError(APIError): - """Client errors (4xx status codes).""" - pass - -class ServerError(APIError): - """Server errors (5xx status codes).""" - pass -``` - -## Type System - -The SDK provides comprehensive type coverage with 695+ type classes organized into categories including: - -- **Enumerations** (50+): HarmCategory, HarmBlockThreshold, FinishReason, BlockedReason, etc. -- **Content Types** (20+): Content, Part, Blob, Image, Video, FileData, etc. -- **Function Calling** (15+): Tool, FunctionDeclaration, FunctionCall, FunctionResponse, etc. -- **Retrieval & RAG** (25+): GoogleSearchRetrieval, FileSearch, VertexAISearch, VertexRagStore, etc. -- **Generation Config** (30+): GenerateContentConfig, GenerationConfig, SafetySetting, etc. -- **Response Types** (40+): GenerateContentResponse, Candidate, SafetyRating, GroundingMetadata, etc. -- **Embeddings** (10+): EmbedContentConfig, EmbedContentResponse, ContentEmbedding, etc. -- **Image Generation** (40+): GenerateImagesConfig, EditImageConfig, UpscaleImageConfig, etc. -- **Video Generation** (20+): GenerateVideosConfig, GenerateVideosResponse, GeneratedVideo, etc. -- **Model Management** (20+): Model, TunedModelInfo, Endpoint, etc. -- **Tuning** (30+): TuningJob, TuningDataset, SupervisedTuningSpec, HyperParameters, etc. -- **Batches** (20+): BatchJob, CreateBatchJobConfig, etc. -- **Caching** (15+): CachedContent, CreateCachedContentConfig, etc. -- **Files** (20+): File, FileStatus, UploadFileConfig, etc. -- **Live API** (60+): LiveConnectConfig, LiveClientMessage, LiveServerMessage, etc. - -All types are available via `from google.genai import types` and include both Pydantic models and TypedDict variants for flexible usage. diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/live.md b/.tessl/tiles/tessl/pypi-google-genai/docs/live.md deleted file mode 100644 index 0a8237613..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/live.md +++ /dev/null @@ -1,473 +0,0 @@ -# Live API - -Real-time bidirectional streaming for interactive applications with support for audio, video, and function calling (Preview). The Live API enables continuous, low-latency communication with the model for conversational AI, voice assistants, and real-time collaboration tools. - -## Capabilities - -### Connect to Live Session - -Establish a bidirectional streaming connection with the model. Returns an async context manager that yields a session for sending and receiving messages. - -```python { .api } -class AsyncLive: - """Asynchronous Live API (Preview).""" - - async def connect( - self, - *, - model: str, - config: Optional[LiveConnectConfig] = None - ) -> AsyncIterator[AsyncSession]: - """ - Connect to live server (async context manager). - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash-exp'). - config (LiveConnectConfig, optional): Connection configuration including: - - system_instruction: System instruction for the session - - generation_config: Generation parameters - - tools: Function declarations for function calling - - speech_config: Speech synthesis configuration - - Yields: - AsyncSession: Active session for bidirectional communication. - - Raises: - ClientError: For client errors - ServerError: For server errors - - Usage: - async with client.aio.live.connect(model='gemini-2.0-flash-exp') as session: - # Send and receive messages - pass - """ - ... -``` - -**Usage Example - Basic Live Session:** - -```python -import asyncio -from google.genai import Client - -async def live_session(): - client = Client(api_key='YOUR_API_KEY') - - async with client.aio.live.connect( - model='gemini-2.0-flash-exp' - ) as session: - # Send message - await session.send_client_content( - turns=[Content(parts=[Part(text='Hello, how are you?')])], - turn_complete=True - ) - - # Receive responses - async for message in session.receive(): - if message.server_content: - if message.server_content.model_turn: - for part in message.server_content.model_turn.parts: - if part.text: - print(part.text, end='', flush=True) - if message.server_content.turn_complete: - print() # New line after turn completes - break - -asyncio.run(live_session()) -``` - -### Send Client Content - -Send conversational content (text, images, etc.) to the model during a live session. - -```python { .api } -class AsyncSession: - """Live session for bidirectional streaming (Preview).""" - - async def send_client_content( - self, - *, - turns: Optional[Union[Content, list[Content]]] = None, - turn_complete: bool = False - ) -> None: - """ - Send client content to the model. - - Parameters: - turns (Union[Content, list[Content]], optional): Content to send. Can be - text, images, or other supported content types. - turn_complete (bool): Whether this completes the current turn. If True, - model will begin generating response. Default: False. - - Raises: - RuntimeError: If session is closed or invalid state - """ - ... -``` - -### Send Realtime Input - -Send realtime media chunks (audio, video) for streaming input. - -```python { .api } -class AsyncSession: - """Live session for bidirectional streaming (Preview).""" - - async def send_realtime_input( - self, - *, - media_chunks: Optional[Sequence[Blob]] = None - ) -> None: - """ - Send realtime media input (audio/video chunks). - - Parameters: - media_chunks (Sequence[Blob], optional): Media chunks to send. Each Blob - contains binary data with MIME type (e.g., 'audio/pcm' for raw audio). - - Raises: - RuntimeError: If session is closed or invalid state - """ - ... -``` - -**Usage Example - Audio Streaming:** - -```python -import asyncio -from google.genai import Client -from google.genai.types import Blob - -async def stream_audio(): - client = Client(api_key='YOUR_API_KEY') - - async with client.aio.live.connect( - model='gemini-2.0-flash-exp' - ) as session: - # Stream audio chunks - with open('audio.pcm', 'rb') as f: - while chunk := f.read(4096): - await session.send_realtime_input( - media_chunks=[Blob( - mime_type='audio/pcm', - data=chunk - )] - ) - - # Receive responses - async for message in session.receive(): - # Process responses - pass - -asyncio.run(stream_audio()) -``` - -### Send Tool Response - -Send function execution results back to the model after it requests a function call. - -```python { .api } -class AsyncSession: - """Live session for bidirectional streaming (Preview).""" - - async def send_tool_response( - self, - *, - function_responses: Sequence[FunctionResponse] - ) -> None: - """ - Send tool/function execution responses. - - Parameters: - function_responses (Sequence[FunctionResponse]): Results from executing - functions that the model requested. - - Raises: - RuntimeError: If session is closed or invalid state - """ - ... -``` - -### Receive Messages - -Receive streaming messages from the model including content, function calls, and metadata. - -```python { .api } -class AsyncSession: - """Live session for bidirectional streaming (Preview).""" - - async def receive(self) -> AsyncIterator[LiveServerMessage]: - """ - Receive messages from the model as an async iterator. - - Yields: - LiveServerMessage: Server messages including: - - setup_complete: Connection setup confirmation - - server_content: Generated content from model - - tool_call: Function call requests from model - - tool_call_cancellation: Cancelled function calls - - Raises: - RuntimeError: If session is closed - """ - ... -``` - -### Close Session - -Explicitly close the live session. - -```python { .api } -class AsyncSession: - """Live session for bidirectional streaming (Preview).""" - - async def close(self) -> None: - """ - Close the live session. - - Note: Sessions are automatically closed when exiting the context manager. - """ - ... -``` - -**Usage Example - Function Calling:** - -```python -import asyncio -from google.genai import Client -from google.genai.types import ( - LiveConnectConfig, - Tool, - FunctionDeclaration, - Schema, - Type, - Content, - Part, - FunctionResponse -) - -async def live_with_functions(): - client = Client(api_key='YOUR_API_KEY') - - # Define function - weather_func = FunctionDeclaration( - name='get_weather', - description='Get weather for a location', - parameters=Schema( - type=Type.OBJECT, - properties={ - 'location': Schema(type=Type.STRING) - } - ) - ) - - config = LiveConnectConfig( - tools=[Tool(function_declarations=[weather_func])] - ) - - async with client.aio.live.connect( - model='gemini-2.0-flash-exp', - config=config - ) as session: - # Ask for weather - await session.send_client_content( - turns=[Content(parts=[Part(text='What is the weather in Tokyo?')])], - turn_complete=True - ) - - # Handle responses - async for message in session.receive(): - if message.tool_call: - # Execute function - if message.tool_call.function_calls: - for fc in message.tool_call.function_calls: - if fc.name == 'get_weather': - # Execute and return result - await session.send_tool_response( - function_responses=[FunctionResponse( - name='get_weather', - response={'temperature': 22, 'condition': 'sunny'}, - id=fc.id - )] - ) - - if message.server_content: - if message.server_content.model_turn: - for part in message.server_content.model_turn.parts: - if part.text: - print(part.text) - if message.server_content.turn_complete: - break - -asyncio.run(live_with_functions()) -``` - -## Types - -```python { .api } -from typing import Optional, Union, Sequence, AsyncIterator, Any -from enum import Enum - -# Configuration types -class LiveConnectConfig: - """ - Configuration for live connection. - - Attributes: - system_instruction (Union[str, Content], optional): System instruction. - generation_config (GenerationConfig, optional): Generation parameters. - tools (list[Tool], optional): Function declarations. - speech_config (SpeechConfig, optional): Speech synthesis configuration. - response_modalities (list[str], optional): Desired response modalities ('TEXT', 'AUDIO'). - """ - system_instruction: Optional[Union[str, Content]] = None - generation_config: Optional[GenerationConfig] = None - tools: Optional[list[Tool]] = None - speech_config: Optional[SpeechConfig] = None - response_modalities: Optional[list[str]] = None - -class SpeechConfig: - """ - Speech synthesis configuration. - - Attributes: - voice_config (VoiceConfig, optional): Voice settings. - """ - voice_config: Optional[VoiceConfig] = None - -class VoiceConfig: - """ - Voice configuration. - - Attributes: - prebuilt_voice_config (PrebuiltVoiceConfig, optional): Prebuilt voice. - """ - prebuilt_voice_config: Optional[PrebuiltVoiceConfig] = None - -class PrebuiltVoiceConfig: - """ - Prebuilt voice settings. - - Attributes: - voice_name (str, optional): Voice name (e.g., 'Aoede', 'Charon'). - """ - voice_name: Optional[str] = None - -# Message types -class LiveServerMessage: - """ - Message from server in live session. - - Attributes: - setup_complete (LiveServerSetupComplete, optional): Setup confirmation. - server_content (LiveServerContent, optional): Generated content. - tool_call (LiveServerToolCall, optional): Function call request. - tool_call_cancellation (LiveServerToolCallCancellation, optional): Cancelled call. - """ - setup_complete: Optional[LiveServerSetupComplete] = None - server_content: Optional[LiveServerContent] = None - tool_call: Optional[LiveServerToolCall] = None - tool_call_cancellation: Optional[LiveServerToolCallCancellation] = None - -class LiveServerSetupComplete: - """Setup complete confirmation.""" - pass - -class LiveServerContent: - """ - Server content message. - - Attributes: - model_turn (Content, optional): Model's turn content. - turn_complete (bool, optional): Whether turn is complete. - interrupted (bool, optional): Whether turn was interrupted. - grounding_metadata (GroundingMetadata, optional): Grounding information. - """ - model_turn: Optional[Content] = None - turn_complete: Optional[bool] = None - interrupted: Optional[bool] = None - grounding_metadata: Optional[GroundingMetadata] = None - -class LiveServerToolCall: - """ - Tool call request from server. - - Attributes: - function_calls (list[FunctionCall], optional): Requested function calls. - """ - function_calls: Optional[list[FunctionCall]] = None - -class LiveServerToolCallCancellation: - """ - Tool call cancellation. - - Attributes: - ids (list[str], optional): IDs of cancelled calls. - """ - ids: Optional[list[str]] = None - -# Core types (shared with other capabilities) -class Content: - """Content container.""" - parts: list[Part] - role: Optional[str] = None - -class Part: - """Content part.""" - text: Optional[str] = None - inline_data: Optional[Blob] = None - function_call: Optional[FunctionCall] = None - function_response: Optional[FunctionResponse] = None - -class Blob: - """Binary data.""" - mime_type: str - data: bytes - -class FunctionCall: - """Function call from model.""" - name: str - args: dict[str, Any] - id: Optional[str] = None - -class FunctionResponse: - """Function execution result.""" - name: str - response: dict[str, Any] - id: Optional[str] = None - -class Tool: - """Tool with function declarations.""" - function_declarations: Optional[list[FunctionDeclaration]] = None - -class FunctionDeclaration: - """Function definition.""" - name: str - description: str - parameters: Optional[Schema] = None - -class Schema: - """JSON schema.""" - type: Type - properties: Optional[dict[str, Schema]] = None - -class Type(Enum): - """JSON schema types.""" - OBJECT = 'OBJECT' - STRING = 'STRING' - NUMBER = 'NUMBER' - INTEGER = 'INTEGER' - BOOLEAN = 'BOOLEAN' - ARRAY = 'ARRAY' - -class GenerationConfig: - """Generation configuration.""" - temperature: Optional[float] = None - top_p: Optional[float] = None - top_k: Optional[int] = None - max_output_tokens: Optional[int] = None - -class GroundingMetadata: - """Grounding metadata.""" - pass -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/models.md b/.tessl/tiles/tessl/pypi-google-genai/docs/models.md deleted file mode 100644 index 3a5a934ca..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/models.md +++ /dev/null @@ -1,355 +0,0 @@ -# Model Information and Management - -Retrieve and manage model information and capabilities. Access model metadata, update model settings, delete tuned models, and list available models. - -## Capabilities - -### Get Model - -Retrieve information about a specific model including capabilities and configuration. - -```python { .api } -def get( - self, - *, - model: str, - config: Optional[GetModelConfig] = None -) -> Model: - """ - Get model information. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash', 'gemini-1.5-pro'). - For tuned models, use full resource name. - config (GetModelConfig, optional): Get configuration. - - Returns: - Model: Model information including: - - Supported generation methods - - Input/output token limits - - Supported features (function calling, multimodal, etc.) - - Model version - - Raises: - ClientError: For client errors including 404 if model not found - ServerError: For server errors (5xx status codes) - """ - ... - -async def get( - self, - *, - model: str, - config: Optional[GetModelConfig] = None -) -> Model: - """Async version of get.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Get model info -model = client.models.get(model='gemini-2.0-flash') - -print(f"Model: {model.name}") -print(f"Display name: {model.display_name}") -print(f"Description: {model.description}") -print(f"Input token limit: {model.input_token_limit}") -print(f"Output token limit: {model.output_token_limit}") -print(f"Supported methods: {model.supported_generation_methods}") - -# Check capabilities -if 'generateContent' in model.supported_generation_methods: - print("Supports content generation") -if 'embedContent' in model.supported_generation_methods: - print("Supports embeddings") -``` - -### Update Model - -Update mutable model metadata such as display name and description (for tuned models). - -```python { .api } -def update( - self, - *, - model: str, - config: UpdateModelConfig -) -> Model: - """ - Update model metadata (tuned models only). - - Parameters: - model (str): Model resource name. - config (UpdateModelConfig): Update configuration including: - - display_name: New display name - - description: New description - - Returns: - Model: Updated model information. - - Raises: - ClientError: For client errors - ServerError: For server errors - """ - ... - -async def update( - self, - *, - model: str, - config: UpdateModelConfig -) -> Model: - """Async version of update.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import UpdateModelConfig - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -config = UpdateModelConfig( - display_name='My Custom Model v2', - description='Updated description' -) - -updated_model = client.models.update( - model='projects/.../locations/.../models/my-model', - config=config -) - -print(f"Updated: {updated_model.display_name}") -``` - -### Delete Model - -Delete a tuned model. - -```python { .api } -def delete( - self, - *, - model: str, - config: Optional[DeleteModelConfig] = None -) -> DeleteModelResponse: - """ - Delete a tuned model. - - Parameters: - model (str): Model resource name to delete. - config (DeleteModelConfig, optional): Delete configuration. - - Returns: - DeleteModelResponse: Deletion confirmation. - - Raises: - ClientError: For client errors including 404 if model not found - ServerError: For server errors - """ - ... - -async def delete( - self, - *, - model: str, - config: Optional[DeleteModelConfig] = None -) -> DeleteModelResponse: - """Async version of delete.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -response = client.models.delete( - model='projects/.../locations/.../models/my-old-model' -) - -print("Model deleted") -``` - -### List Models - -List all available models with optional pagination and filtering. - -```python { .api } -def list( - self, - *, - config: Optional[ListModelsConfig] = None -) -> Union[Pager[Model], Iterator[Model]]: - """ - List available models. - - Parameters: - config (ListModelsConfig, optional): List configuration including: - - page_size: Number of models per page - - page_token: Token for pagination - - filter: Filter expression - - Returns: - Union[Pager[Model], Iterator[Model]]: Paginated model list. - - Raises: - ClientError: For client errors - ServerError: For server errors - """ - ... - -async def list( - self, - *, - config: Optional[ListModelsConfig] = None -) -> Union[AsyncPager[Model], AsyncIterator[Model]]: - """Async version of list.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# List all available models -print("Available models:") -for model in client.models.list(): - print(f"- {model.name}: {model.display_name}") - print(f" Methods: {', '.join(model.supported_generation_methods)}") - -# List with pagination -from google.genai.types import ListModelsConfig - -config = ListModelsConfig(page_size=10) -pager = client.models.list(config=config) - -print(f"\nFirst page ({len(pager.page)} models):") -for model in pager.page: - print(f"- {model.name}") -``` - -## Types - -```python { .api } -from typing import Optional, List, Iterator, AsyncIterator, Union - -# Configuration types -class GetModelConfig: - """Configuration for getting model.""" - pass - -class UpdateModelConfig: - """ - Configuration for updating model. - - Attributes: - display_name (str, optional): New display name. - description (str, optional): New description. - """ - display_name: Optional[str] = None - description: Optional[str] = None - -class DeleteModelConfig: - """Configuration for deleting model.""" - pass - -class ListModelsConfig: - """ - Configuration for listing models. - - Attributes: - page_size (int, optional): Number of models per page. - page_token (str, optional): Token for pagination. - filter (str, optional): Filter expression. - """ - page_size: Optional[int] = None - page_token: Optional[str] = None - filter: Optional[str] = None - -# Response types -class Model: - """ - Model information and capabilities. - - Attributes: - name (str): Model resource name (e.g., 'models/gemini-2.0-flash'). - base_model_id (str, optional): Base model identifier. - version (str, optional): Model version. - display_name (str): Human-readable display name. - description (str): Model description. - input_token_limit (int): Maximum input tokens. - output_token_limit (int): Maximum output tokens. - supported_generation_methods (list[str]): Supported methods: - - 'generateContent': Text/multimodal generation - - 'embedContent': Embeddings - - 'generateImages': Image generation - - 'generateVideos': Video generation - temperature (float, optional): Default temperature. - top_p (float, optional): Default top_p. - top_k (int, optional): Default top_k. - max_temperature (float, optional): Maximum allowed temperature. - tuned_model_info (TunedModelInfo, optional): Info for tuned models. - """ - name: str - base_model_id: Optional[str] = None - version: Optional[str] = None - display_name: str - description: str - input_token_limit: int - output_token_limit: int - supported_generation_methods: list[str] - temperature: Optional[float] = None - top_p: Optional[float] = None - top_k: Optional[int] = None - max_temperature: Optional[float] = None - tuned_model_info: Optional[TunedModelInfo] = None - -class TunedModelInfo: - """ - Information for tuned models. - - Attributes: - tuning_job (str): Tuning job that created this model. - base_model (str): Base model used for tuning. - tuning_dataset (str, optional): Training dataset. - """ - tuning_job: str - base_model: str - tuning_dataset: Optional[str] = None - -class DeleteModelResponse: - """ - Response from deleting a model. - - Attributes: - deleted (bool): Whether deletion succeeded. - """ - deleted: bool - -# Pager types -class Pager[T]: - """Synchronous pager.""" - page: list[T] - def next_page(self) -> None: ... - def __iter__(self) -> Iterator[T]: ... - -class AsyncPager[T]: - """Asynchronous pager.""" - page: list[T] - async def next_page(self) -> None: ... - async def __aiter__(self) -> AsyncIterator[T]: ... -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/operations.md b/.tessl/tiles/tessl/pypi-google-genai/docs/operations.md deleted file mode 100644 index 0ce73223b..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/operations.md +++ /dev/null @@ -1,290 +0,0 @@ -# Long-Running Operations - -Monitor and retrieve status of long-running operations like video generation, file imports, and other asynchronous tasks. Operations provide a way to track progress and retrieve results for tasks that take significant time to complete. - -## Capabilities - -### Get Operation - -Retrieve the current status and result of a long-running operation. - -```python { .api } -class Operations: - """Synchronous long-running operations API.""" - - def get(self, operation: Union[Operation, str]) -> Operation: - """ - Get operation status and result. - - Parameters: - operation (Union[Operation, str]): Operation to check. Can be: - - Operation: Operation object to refresh - - str: Operation name/resource name - - Returns: - Operation: Updated operation with current status. Check operation.done - to see if complete. If done, check operation.error for errors or - operation.response for results. - - Raises: - ClientError: For client errors including 404 if operation not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncOperations: - """Asynchronous long-running operations API.""" - - async def get(self, operation: Union[Operation, str]) -> Operation: - """Async version of get.""" - ... -``` - -**Usage Example - Video Generation:** - -```python -import time -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Start video generation -operation = client.models.generate_videos( - model='veo-2.0-generate-001', - prompt='A cat playing with a ball' -) - -print(f"Operation started: {operation.name}") -print(f"Done: {operation.done}") - -# Poll for completion -while not operation.done: - print("Waiting for completion...") - time.sleep(10) - - # Refresh operation status - operation = client.operations.get(operation) - print(f"Done: {operation.done}") - -# Check result -if operation.error: - print(f"Operation failed: {operation.error.message}") -else: - print("Operation succeeded!") - response = operation.response - - # Access video generation response - for i, video in enumerate(response.generated_videos): - with open(f'video_{i}.mp4', 'wb') as f: - f.write(video.video.data) - print(f"Saved video_{i}.mp4") -``` - -**Usage Example - File Import:** - -```python -from google.genai import Client -from google.genai.types import ImportFileSource - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Start file import -source = ImportFileSource(gcs_uri='gs://my-bucket/document.pdf') -operation = client.file_search_stores.import_file( - store='fileSearchStores/abc123', - source=source -) - -print(f"Import operation: {operation.name}") - -# Poll until done -while not operation.done: - import time - time.sleep(5) - operation = client.operations.get(operation) - -if operation.error: - print(f"Import failed: {operation.error.message}") -else: - print("Import completed successfully") -``` - -**Usage Example - Async Polling:** - -```python -import asyncio -from google.genai import Client - -async def wait_for_operation(): - client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - - # Start operation - operation = await client.aio.models.generate_videos( - model='veo-2.0-generate-001', - prompt='A sunset over mountains' - ) - - print(f"Operation: {operation.name}") - - # Poll asynchronously - while not operation.done: - print("Checking status...") - await asyncio.sleep(10) - operation = await client.aio.operations.get(operation) - - if operation.error: - print(f"Failed: {operation.error.message}") - else: - print(f"Success! Generated {len(operation.response.generated_videos)} video(s)") - -asyncio.run(wait_for_operation()) -``` - -**Usage Example - Operation Metadata:** - -```python -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Get operation -operation = client.operations.get('operations/abc123') - -print(f"Operation: {operation.name}") -print(f"Done: {operation.done}") - -# Check metadata for progress -if operation.metadata: - print(f"Metadata: {operation.metadata}") - # Metadata structure varies by operation type - # May include progress percentage, timestamps, etc. - -# Check if operation completed -if operation.done: - if operation.error: - print(f"Error code: {operation.error.code}") - print(f"Error message: {operation.error.message}") - if operation.error.details: - print(f"Details: {operation.error.details}") - else: - print("Operation completed successfully") - # Access operation.response for results -``` - -## Types - -```python { .api } -from typing import Optional, Union, Any, Dict - -# Operation base class -class Operation: - """ - Long-running operation. - - This is an abstract base class. Specific operation types inherit from it - (e.g., GenerateVideosOperation, ImportFileOperation). - - Attributes: - name (str): Operation resource name for polling. - Format varies by operation type (e.g., 'operations/{id}'). - done (bool): Whether operation has completed (successfully or with error). - error (OperationError, optional): Error if operation failed. Only set if - done=True and operation failed. - response (Any, optional): Operation result if successful. Only set if - done=True and operation succeeded. Type depends on operation: - - GenerateVideosOperation.response: GenerateVideosResponse - - ImportFileOperation.response: ImportFileResponse - - etc. - metadata (Dict[str, Any], optional): Operation metadata including: - - Progress information - - Timestamps - - Operation-specific data - Structure varies by operation type. - """ - name: str - done: bool - error: Optional[OperationError] = None - response: Optional[Any] = None - metadata: Optional[Dict[str, Any]] = None - -class OperationError: - """ - Error information for failed operation. - - Attributes: - code (int): Error code (HTTP status code or gRPC code). - message (str): Human-readable error message. - details (list, optional): Additional error details. Structure varies by - error type and may include: - - Error metadata - - Retry information - - Debugging information - """ - code: int - message: str - details: Optional[list] = None - -# Specific operation types (inherit from Operation) -class GenerateVideosOperation(Operation): - """ - Video generation operation. - - Attributes: - response (GenerateVideosResponse, optional): Video generation response when done. - """ - response: Optional[GenerateVideosResponse] = None - -class ImportFileOperation(Operation): - """ - File import operation. - - Attributes: - response (Dict, optional): Import response when done. - """ - response: Optional[Dict] = None - -class UploadToFileSearchStoreOperation(Operation): - """ - File upload operation for file search stores. - - Attributes: - response (Dict, optional): Upload response when done. - """ - response: Optional[Dict] = None - -# Response types (referenced by operations) -class GenerateVideosResponse: - """ - Video generation response (from GenerateVideosOperation). - - Attributes: - generated_videos (list[GeneratedVideo]): Generated videos. - rai_media_filtered_count (int, optional): Safety-filtered count. - rai_media_filtered_reasons (list[str], optional): Filter reasons. - """ - generated_videos: list[GeneratedVideo] - rai_media_filtered_count: Optional[int] = None - rai_media_filtered_reasons: Optional[list[str]] = None - -class GeneratedVideo: - """ - Generated video with metadata. - - Attributes: - video (Video): Video object with data. - generation_seed (int, optional): Generation seed. - """ - video: Video - generation_seed: Optional[int] = None - -class Video: - """ - Video data. - - Attributes: - data (bytes): Video binary data. - mime_type (str): MIME type (e.g., 'video/mp4'). - """ - data: bytes - mime_type: str -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/tokens.md b/.tessl/tiles/tessl/pypi-google-genai/docs/tokens.md deleted file mode 100644 index 656b1631c..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/tokens.md +++ /dev/null @@ -1,328 +0,0 @@ -# Token Operations and Local Tokenizer - -Count tokens and compute detailed token information for content, with support for local tokenization without API calls. Token counting helps estimate costs, manage context limits, and optimize prompts. - -## Capabilities - -### Count Tokens - -Count the number of tokens in content using the API. - -```python { .api } -def count_tokens( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[CountTokensConfig] = None -) -> CountTokensResponse: - """ - Count tokens in content via API. - - Parameters: - model (str): Model identifier (e.g., 'gemini-2.0-flash'). - contents (Union[str, list[Content], Content]): Content to count tokens for. - config (CountTokensConfig, optional): Configuration including: - - generation_config: Generation config to count with - - system_instruction: System instruction to include - - tools: Tools to include - - Returns: - CountTokensResponse: Token count including prompt tokens. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def count_tokens( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[CountTokensConfig] = None -) -> CountTokensResponse: - """Async version of count_tokens.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -# Count tokens in text -response = client.models.count_tokens( - model='gemini-2.0-flash', - contents='How many tokens is this sentence?' -) - -print(f"Total tokens: {response.total_tokens}") - -# Count tokens with context -from google.genai.types import CountTokensConfig, Content, Part - -config = CountTokensConfig( - system_instruction='You are a helpful assistant.' -) - -response = client.models.count_tokens( - model='gemini-2.0-flash', - contents=[ - Content(role='user', parts=[Part(text='Hello')]), - Content(role='model', parts=[Part(text='Hi there!')]), - Content(role='user', parts=[Part(text='How are you?')]) - ], - config=config -) - -print(f"Total tokens including context: {response.total_tokens}") -``` - -### Compute Tokens - -Compute detailed token information including token IDs via API. - -```python { .api } -def compute_tokens( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[ComputeTokensConfig] = None -) -> ComputeTokensResponse: - """ - Compute detailed token information via API. - - Parameters: - model (str): Model identifier. - contents (Union[str, list[Content], Content]): Content to tokenize. - config (ComputeTokensConfig, optional): Configuration. - - Returns: - ComputeTokensResponse: Detailed token information including token IDs. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def compute_tokens( - *, - model: str, - contents: Union[str, list[Content], Content], - config: Optional[ComputeTokensConfig] = None -) -> ComputeTokensResponse: - """Async version of compute_tokens.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(api_key='YOUR_API_KEY') - -response = client.models.compute_tokens( - model='gemini-2.0-flash', - contents='Hello world' -) - -print(f"Total tokens: {response.total_tokens}") - -for token_info in response.tokens_info: - for token in token_info.tokens: - print(f"Token: '{token.token}' (ID: {token.token_id})") -``` - -### Local Tokenizer - -Perform token counting locally without API calls (Experimental). The local tokenizer downloads model tokenizer data once and performs tokenization locally, useful for offline scenarios or reducing API calls. - -```python { .api } -class LocalTokenizer: - """Local tokenizer for text-only token counting (Experimental).""" - - def __init__(self, model_name: str): - """ - Initialize local tokenizer for a model. - - Parameters: - model_name (str): Model name (e.g., 'gemini-2.0-flash'). - Downloads tokenizer data on first use. - - Raises: - ValueError: If model doesn't support local tokenization - """ - ... - - def count_tokens( - self, - contents: Union[str, list[Content], Content], - *, - config: Optional[CountTokensConfig] = None - ) -> CountTokensResult: - """ - Count tokens locally (text-only). - - Parameters: - contents (Union[str, list[Content], Content]): Content to count. - Only text parts are counted; images/audio are ignored. - config (CountTokensConfig, optional): Configuration. - - Returns: - CountTokensResult: Token count. Note: May differ slightly from API - count due to local approximation. - """ - ... - - def compute_tokens( - self, - contents: Union[str, list[Content], Content] - ) -> ComputeTokensResult: - """ - Compute detailed token information locally. - - Parameters: - contents (Union[str, list[Content], Content]): Content to tokenize. - - Returns: - ComputeTokensResult: Detailed token information with IDs. - """ - ... -``` - -**Usage Example:** - -```python -from google.genai.local_tokenizer import LocalTokenizer - -# Initialize tokenizer (downloads data on first use) -tokenizer = LocalTokenizer('gemini-2.0-flash') - -# Count tokens locally -result = tokenizer.count_tokens('How many tokens is this?') -print(f"Total tokens (local): {result.total_tokens}") - -# Compute detailed tokens -result = tokenizer.compute_tokens('Hello world') -for token_info in result.tokens_info: - for token in token_info.tokens: - print(f"Token: '{token.token}' (ID: {token.token_id})") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List - -# Configuration types -class CountTokensConfig: - """ - Configuration for token counting. - - Attributes: - generation_config (GenerationConfig, optional): Generation config to include. - system_instruction (Union[str, Content], optional): System instruction to include. - tools (list[Tool], optional): Tools to include in count. - """ - generation_config: Optional[GenerationConfig] = None - system_instruction: Optional[Union[str, Content]] = None - tools: Optional[list[Tool]] = None - -class ComputeTokensConfig: - """Configuration for computing tokens.""" - pass - -# Response types -class CountTokensResponse: - """ - Response from count_tokens API. - - Attributes: - total_tokens (int): Total number of tokens. - total_billable_characters (int, optional): Billable characters count (for embeddings). - """ - total_tokens: int - total_billable_characters: Optional[int] = None - -class ComputeTokensResponse: - """ - Response from compute_tokens API. - - Attributes: - tokens_info (list[TokensInfo]): Detailed token information per content part. - total_tokens (int): Total number of tokens. - """ - tokens_info: list[TokensInfo] - total_tokens: int - -class TokensInfo: - """ - Token information for content. - - Attributes: - tokens (list[Token]): Individual tokens. - role (str, optional): Content role. - """ - tokens: list[Token] - role: Optional[str] = None - -class Token: - """ - Individual token. - - Attributes: - token (str): Token string. - token_id (int): Token ID in model vocabulary. - """ - token: str - token_id: int - -# Local tokenizer result types -class CountTokensResult: - """ - Result from local token counting. - - Attributes: - total_tokens (int): Total number of tokens. - """ - total_tokens: int - -class ComputeTokensResult: - """ - Result from local token computation. - - Attributes: - tokens_info (list[TokensInfo]): Detailed token information. - total_tokens (int): Total number of tokens. - """ - tokens_info: list[TokensInfo] - total_tokens: int - -# Core types (shared) -class Content: - """Content container.""" - parts: list[Part] - role: Optional[str] = None - -class Part: - """Content part.""" - text: Optional[str] = None - inline_data: Optional[Blob] = None - -class Blob: - """Binary data.""" - mime_type: str - data: bytes - -class GenerationConfig: - """Generation configuration.""" - pass - -class Tool: - """Tool definition.""" - pass -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/tuning.md b/.tessl/tiles/tessl/pypi-google-genai/docs/tuning.md deleted file mode 100644 index c8c53f49a..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/tuning.md +++ /dev/null @@ -1,405 +0,0 @@ -# Model Fine-Tuning - -Create and manage supervised fine-tuning jobs to customize models on your data (Vertex AI only). Fine-tuning allows you to adapt foundation models to your specific use case by training on your own datasets, improving model performance for domain-specific tasks. - -## Capabilities - -### Tune Model - -Create a supervised fine-tuning job to customize a base model. - -```python { .api } -class Tunings: - """Synchronous tuning jobs API (Vertex AI only).""" - - def tune( - self, - *, - base_model: str, - training_dataset: TuningDataset, - config: Optional[CreateTuningJobConfig] = None - ) -> TuningJob: - """ - Create a supervised fine-tuning job. - - Parameters: - base_model (str): Base model to fine-tune (e.g., 'gemini-1.5-flash-002'). - training_dataset (TuningDataset): Training dataset specification including - GCS URI or inline examples. - config (CreateTuningJobConfig, optional): Tuning configuration including: - - tuned_model_display_name: Display name for tuned model - - epoch_count: Number of training epochs - - learning_rate: Learning rate - - adapter_size: Adapter size for efficient tuning - - validation_dataset: Optional validation dataset - - Returns: - TuningJob: Created tuning job with name and initial status. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncTunings: - """Asynchronous tuning jobs API (Vertex AI only).""" - - async def tune( - self, - *, - base_model: str, - training_dataset: TuningDataset, - config: Optional[CreateTuningJobConfig] = None - ) -> TuningJob: - """Async version of tune.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client -from google.genai.types import ( - TuningDataset, - CreateTuningJobConfig, - HyperParameters, - AdapterSize -) - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Define training dataset -training_dataset = TuningDataset( - gcs_uri='gs://my-bucket/training_data.jsonl' -) - -# Configure tuning -config = CreateTuningJobConfig( - tuned_model_display_name='my-custom-model', - epoch_count=5, - learning_rate_multiplier=1.0, - adapter_size=AdapterSize.ADAPTER_SIZE_SIXTEEN, - validation_dataset=TuningDataset( - gcs_uri='gs://my-bucket/validation_data.jsonl' - ) -) - -# Start tuning -job = client.tunings.tune( - base_model='gemini-1.5-flash-002', - training_dataset=training_dataset, - config=config -) - -print(f"Tuning job created: {job.name}") -print(f"State: {job.state}") - -# Poll for completion -import time -while job.state in ['JOB_STATE_PENDING', 'JOB_STATE_RUNNING']: - time.sleep(60) - job = client.tunings.get(name=job.name) - print(f"State: {job.state}") - -if job.state == 'JOB_STATE_SUCCEEDED': - print(f"Tuned model: {job.tuned_model.model}") - - # Use tuned model - response = client.models.generate_content( - model=job.tuned_model.model, - contents='Test the tuned model' - ) - print(response.text) -``` - -### Get Tuning Job - -Retrieve information about a tuning job including status and progress. - -```python { .api } -class Tunings: - """Synchronous tuning jobs API (Vertex AI only).""" - - def get( - self, - *, - name: str, - config: Optional[GetTuningJobConfig] = None - ) -> TuningJob: - """ - Get tuning job information. - - Parameters: - name (str): Job name in format 'projects/*/locations/*/tuningJobs/*'. - config (GetTuningJobConfig, optional): Get configuration. - - Returns: - TuningJob: Job information including state, progress, and tuned model. - - Raises: - ClientError: For client errors including 404 if job not found - ServerError: For server errors (5xx status codes) - """ - ... - -class AsyncTunings: - """Asynchronous tuning jobs API (Vertex AI only).""" - - async def get( - self, - *, - name: str, - config: Optional[GetTuningJobConfig] = None - ) -> TuningJob: - """Async version of get.""" - ... -``` - -### Cancel Tuning Job - -Cancel a running tuning job. - -```python { .api } -class Tunings: - """Synchronous tuning jobs API (Vertex AI only).""" - - def cancel(self, *, name: str) -> None: - """ - Cancel a tuning job. - - Parameters: - name (str): Job name in format 'projects/*/locations/*/tuningJobs/*'. - - Raises: - ClientError: For client errors - ServerError: For server errors - """ - ... - -class AsyncTunings: - """Asynchronous tuning jobs API (Vertex AI only).""" - - async def cancel(self, *, name: str) -> None: - """Async version of cancel.""" - ... -``` - -### List Tuning Jobs - -List all tuning jobs with optional filtering and pagination. - -```python { .api } -class Tunings: - """Synchronous tuning jobs API (Vertex AI only).""" - - def list( - self, - *, - config: Optional[ListTuningJobsConfig] = None - ) -> Union[Pager[TuningJob], Iterator[TuningJob]]: - """ - List tuning jobs. - - Parameters: - config (ListTuningJobsConfig, optional): List configuration including: - - page_size: Number of jobs per page - - page_token: Token for pagination - - filter: Filter expression - - Returns: - Union[Pager[TuningJob], Iterator[TuningJob]]: Paginated job list. - - Raises: - ClientError: For client errors - ServerError: For server errors - """ - ... - -class AsyncTunings: - """Asynchronous tuning jobs API (Vertex AI only).""" - - async def list( - self, - *, - config: Optional[ListTuningJobsConfig] = None - ) -> Union[AsyncPager[TuningJob], AsyncIterator[TuningJob]]: - """Async version of list.""" - ... -``` - -**Usage Example:** - -```python -from google.genai import Client - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# List all tuning jobs -for job in client.tunings.list(): - print(f"{job.name}: {job.state}") - if job.tuned_model: - print(f" Tuned model: {job.tuned_model.model}") -``` - -## Types - -```python { .api } -from typing import Optional, Union, List, Iterator, AsyncIterator, Dict -from datetime import datetime -from enum import Enum - -# Configuration types -class CreateTuningJobConfig: - """ - Configuration for creating tuning jobs. - - Attributes: - tuned_model_display_name (str, optional): Display name for the tuned model. - epoch_count (int, optional): Number of training epochs (1-100). Default: 5. - learning_rate_multiplier (float, optional): Learning rate multiplier (0.001-10.0). - Default: 1.0. - adapter_size (AdapterSize, optional): Adapter size for efficient tuning. - Default: ADAPTER_SIZE_ONE. - validation_dataset (TuningDataset, optional): Validation dataset. - labels (dict[str, str], optional): Job labels. - """ - tuned_model_display_name: Optional[str] = None - epoch_count: Optional[int] = None - learning_rate_multiplier: Optional[float] = None - adapter_size: Optional[AdapterSize] = None - validation_dataset: Optional[TuningDataset] = None - labels: Optional[dict[str, str]] = None - -class GetTuningJobConfig: - """Configuration for getting tuning job.""" - pass - -class ListTuningJobsConfig: - """ - Configuration for listing tuning jobs. - - Attributes: - page_size (int, optional): Number of jobs per page. - page_token (str, optional): Token for pagination. - filter (str, optional): Filter expression. - """ - page_size: Optional[int] = None - page_token: Optional[str] = None - filter: Optional[str] = None - -class TuningDataset: - """ - Training or validation dataset specification. - - Attributes: - gcs_uri (str, optional): GCS URI to JSONL file (e.g., 'gs://bucket/data.jsonl'). - Each line should be a JSON object with 'contents' field. - """ - gcs_uri: Optional[str] = None - -# Response types -class TuningJob: - """ - Tuning job information. - - Attributes: - name (str): Job resource name. - base_model (str): Base model being tuned. - tuned_model (TunedModel, optional): Information about the tuned model. - state (JobState): Current job state. - create_time (datetime): When job was created. - start_time (datetime, optional): When job started. - end_time (datetime, optional): When job completed. - update_time (datetime): Last update time. - labels (dict[str, str], optional): Job labels. - tuning_data_stats (TuningDataStats, optional): Training data statistics. - error (JobError, optional): Error if job failed. - """ - name: str - base_model: str - tuned_model: Optional[TunedModel] = None - state: JobState - create_time: datetime - start_time: Optional[datetime] = None - end_time: Optional[datetime] = None - update_time: datetime - labels: Optional[dict[str, str]] = None - tuning_data_stats: Optional[TuningDataStats] = None - error: Optional[JobError] = None - -class TunedModel: - """ - Information about tuned model. - - Attributes: - model (str): Model resource name for use in generation requests. - endpoint (str, optional): Endpoint for the tuned model. - display_name (str, optional): Display name. - """ - model: str - endpoint: Optional[str] = None - display_name: Optional[str] = None - -class TuningDataStats: - """ - Statistics about tuning data. - - Attributes: - training_dataset_size (int, optional): Number of training examples. - validation_dataset_size (int, optional): Number of validation examples. - total_tuning_character_count (int, optional): Total characters in training data. - total_billable_character_count (int, optional): Billable characters. - """ - training_dataset_size: Optional[int] = None - validation_dataset_size: Optional[int] = None - total_tuning_character_count: Optional[int] = None - total_billable_character_count: Optional[int] = None - -class JobState(Enum): - """Tuning job states.""" - JOB_STATE_UNSPECIFIED = 'JOB_STATE_UNSPECIFIED' - JOB_STATE_QUEUED = 'JOB_STATE_QUEUED' - JOB_STATE_PENDING = 'JOB_STATE_PENDING' - JOB_STATE_RUNNING = 'JOB_STATE_RUNNING' - JOB_STATE_SUCCEEDED = 'JOB_STATE_SUCCEEDED' - JOB_STATE_FAILED = 'JOB_STATE_FAILED' - JOB_STATE_CANCELLING = 'JOB_STATE_CANCELLING' - JOB_STATE_CANCELLED = 'JOB_STATE_CANCELLED' - JOB_STATE_PAUSED = 'JOB_STATE_PAUSED' - -class AdapterSize(Enum): - """Adapter sizes for efficient tuning.""" - ADAPTER_SIZE_UNSPECIFIED = 'ADAPTER_SIZE_UNSPECIFIED' - ADAPTER_SIZE_ONE = 'ADAPTER_SIZE_ONE' - ADAPTER_SIZE_FOUR = 'ADAPTER_SIZE_FOUR' - ADAPTER_SIZE_EIGHT = 'ADAPTER_SIZE_EIGHT' - ADAPTER_SIZE_SIXTEEN = 'ADAPTER_SIZE_SIXTEEN' - ADAPTER_SIZE_THIRTY_TWO = 'ADAPTER_SIZE_THIRTY_TWO' - -class JobError: - """ - Job error information. - - Attributes: - code (int): Error code. - message (str): Error message. - details (list[dict], optional): Error details. - """ - code: int - message: str - details: Optional[list[dict]] = None - -# Pager types -class Pager[T]: - """Synchronous pager.""" - page: list[T] - def next_page(self) -> None: ... - def __iter__(self) -> Iterator[T]: ... - -class AsyncPager[T]: - """Asynchronous pager.""" - page: list[T] - async def next_page(self) -> None: ... - async def __aiter__(self) -> AsyncIterator[T]: ... -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md b/.tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md deleted file mode 100644 index ea1c8f51d..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/docs/video-generation.md +++ /dev/null @@ -1,319 +0,0 @@ -# Video Generation - -Generate videos from text prompts, images, or existing videos using Veo models. Video generation is a long-running operation that returns immediately with an operation handle that can be polled for completion status. - -## Capabilities - -### Generate Videos - -Generate videos from text prompts, optionally with reference images or videos. Returns a long-running operation that must be polled for completion. - -```python { .api } -def generate_videos( - *, - model: str, - prompt: Optional[str] = None, - image: Optional[Image] = None, - video: Optional[Video] = None, - config: Optional[GenerateVideosConfig] = None -) -> GenerateVideosOperation: - """ - Generate videos (returns long-running operation). - - Parameters: - model (str): Model identifier (e.g., 'veo-2.0-generate-001', 'veo-001'). - prompt (str, optional): Text description of the video to generate. At least one - of prompt, image, or video must be provided. - image (Image, optional): Reference image for image-to-video generation. - video (Video, optional): Reference video for video-to-video generation. - config (GenerateVideosConfig, optional): Generation configuration including: - - aspect_ratio: Video aspect ratio ('16:9', '9:16', '1:1') - - duration: Video duration in seconds - - negative_prompt: What to avoid - - safety_filter_level: Safety filtering level - - person_generation: Control person generation - - compressed: Use compressed output format - - Returns: - GenerateVideosOperation: Long-running operation handle. Use client.operations.get() - to poll for completion and retrieve the generated videos. - - Raises: - ClientError: For client errors (4xx status codes) - ServerError: For server errors (5xx status codes) - """ - ... - -async def generate_videos( - *, - model: str, - prompt: Optional[str] = None, - image: Optional[Image] = None, - video: Optional[Video] = None, - config: Optional[GenerateVideosConfig] = None -) -> GenerateVideosOperation: - """Async version of generate_videos.""" - ... -``` - -**Usage Example - Text to Video:** - -```python -import time -from google.genai import Client -from google.genai.types import GenerateVideosConfig - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Start video generation -config = GenerateVideosConfig( - aspect_ratio='16:9', - duration=5, # 5 seconds - negative_prompt='blurry, low quality' -) - -operation = client.models.generate_videos( - model='veo-2.0-generate-001', - prompt='A dog running through a field of flowers at sunset', - config=config -) - -print(f"Operation started: {operation.name}") - -# Poll for completion -while True: - operation = client.operations.get(operation) - - if operation.done: - if operation.error: - print(f"Error: {operation.error}") - else: - response = operation.response - print(f"Generated {len(response.generated_videos)} video(s)") - - # Save videos - for i, video in enumerate(response.generated_videos): - with open(f'generated_{i}.mp4', 'wb') as f: - f.write(video.video.data) - break - - print("Still generating...") - time.sleep(10) # Wait 10 seconds before polling again -``` - -**Usage Example - Image to Video:** - -```python -from google.genai import Client -from google.genai.types import Image, GenerateVideosConfig - -client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - -# Load reference image -image = Image.from_file('landscape.jpg') - -config = GenerateVideosConfig( - aspect_ratio='16:9', - duration=8 -) - -# Generate video from image -operation = client.models.generate_videos( - model='veo-2.0-generate-001', - prompt='Camera slowly pans across the landscape', - image=image, - config=config -) - -print(f"Operation: {operation.name}") -# Poll operation.done until complete -``` - -**Usage Example - Async with Polling:** - -```python -import asyncio -from google.genai import Client - -async def generate_and_wait(): - client = Client(vertexai=True, project='PROJECT_ID', location='us-central1') - - # Start generation - operation = await client.aio.models.generate_videos( - model='veo-2.0-generate-001', - prompt='A cat playing with a ball of yarn' - ) - - print(f"Started operation: {operation.name}") - - # Poll until complete - while not operation.done: - await asyncio.sleep(10) - operation = await client.aio.operations.get(operation) - print("Checking status...") - - if operation.error: - print(f"Error: {operation.error}") - else: - print(f"Success! {len(operation.response.generated_videos)} videos generated") - -asyncio.run(generate_and_wait()) -``` - -## Types - -```python { .api } -from typing import Optional, Literal, TypedDict -from enum import Enum - -# Configuration types -class GenerateVideosConfig: - """ - Configuration for video generation. - - Attributes: - aspect_ratio (str, optional): Video aspect ratio. Options: '16:9', '9:16', '1:1'. - Default varies by model. - duration (int, optional): Video duration in seconds. Valid range depends on model. - Typical: 4-8 seconds. - negative_prompt (str, optional): What to avoid in the video. - safety_filter_level (str, optional): Safety filtering level ('block_most', 'block_some', 'block_few'). - person_generation (PersonGeneration, optional): Control person generation in video. - compressed (bool, optional): Use compressed output format for smaller file size. - generation_mode (str, optional): Generation mode (e.g., 'fast', 'quality'). - """ - aspect_ratio: Optional[str] = None - duration: Optional[int] = None - negative_prompt: Optional[str] = None - safety_filter_level: Optional[str] = None - person_generation: Optional[PersonGeneration] = None - compressed: Optional[bool] = None - generation_mode: Optional[str] = None - -# Response types -class GenerateVideosResponse: - """ - Response from completed video generation. - - Accessed via operation.response after operation completes. - - Attributes: - generated_videos (list[GeneratedVideo]): Generated videos with metadata. - rai_media_filtered_count (int, optional): Number of videos filtered by safety. - rai_media_filtered_reasons (list[str], optional): Safety filtering reasons. - """ - generated_videos: list[GeneratedVideo] - rai_media_filtered_count: Optional[int] = None - rai_media_filtered_reasons: Optional[list[str]] = None - -class GeneratedVideo: - """ - Generated video with metadata. - - Attributes: - video (Video): Video object with data. - generation_seed (int, optional): Seed used for generation (for reproducibility). - """ - video: Video - generation_seed: Optional[int] = None - -# Operation types -class GenerateVideosOperation: - """ - Long-running operation for video generation. - - Attributes: - name (str): Operation name/identifier for polling. - done (bool): Whether operation is complete. - error (OperationError, optional): Error if operation failed. - response (GenerateVideosResponse, optional): Response if operation succeeded. - metadata (dict, optional): Operation metadata including progress. - """ - name: str - done: bool - error: Optional[OperationError] = None - response: Optional[GenerateVideosResponse] = None - metadata: Optional[dict] = None - -class OperationError: - """ - Operation error information. - - Attributes: - code (int): Error code. - message (str): Error message. - details (list, optional): Additional error details. - """ - code: int - message: str - details: Optional[list] = None - -# Input types -class Video: - """ - Video data supporting multiple formats. - - Can be created from: - - File: Video.from_file('path.mp4') - - URL: Video.from_url('https://...') - - Bytes: Video.from_bytes(data, mime_type='video/mp4') - - FileData: Video(file_data=FileData(...)) - - Attributes: - data (bytes, optional): Video binary data. - mime_type (str, optional): MIME type (e.g., 'video/mp4'). - file_data (FileData, optional): Reference to uploaded file. - """ - data: Optional[bytes] = None - mime_type: Optional[str] = None - file_data: Optional[FileData] = None - - @staticmethod - def from_file(path: str) -> Video: - """Load video from file path.""" - ... - - @staticmethod - def from_url(url: str) -> Video: - """Load video from URL.""" - ... - - @staticmethod - def from_bytes(data: bytes, mime_type: str) -> Video: - """Create video from bytes.""" - ... - -class Image: - """Image data for image-to-video generation.""" - @staticmethod - def from_file(path: str) -> Image: ... - -class FileData: - """ - Reference to uploaded file. - - Attributes: - file_uri (str): URI (e.g., 'gs://bucket/file') - mime_type (str): MIME type - """ - file_uri: str - mime_type: str - -# Enum types -class PersonGeneration(Enum): - """Person generation control.""" - DONT_ALLOW = 'dont_allow' - ALLOW_ADULT = 'allow_adult' - ALLOW_ALL = 'allow_all' - -# TypedDict variants -class GenerateVideosConfigDict(TypedDict, total=False): - """TypedDict variant of GenerateVideosConfig.""" - aspect_ratio: str - duration: int - negative_prompt: str - safety_filter_level: str - person_generation: PersonGeneration - compressed: bool - generation_mode: str -``` diff --git a/.tessl/tiles/tessl/pypi-google-genai/tile.json b/.tessl/tiles/tessl/pypi-google-genai/tile.json deleted file mode 100644 index 53b88bff9..000000000 --- a/.tessl/tiles/tessl/pypi-google-genai/tile.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "tessl/pypi-google-genai", - "version": "1.51.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/google-genai@1.51.0", - "summary": "GenAI Python SDK for Google's generative models supporting both Gemini Developer API and Vertex AI APIs", - "private": false -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/compute-resources.md b/.tessl/tiles/tessl/pypi-modal/docs/compute-resources.md deleted file mode 100644 index 2c597a024..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/compute-resources.md +++ /dev/null @@ -1,490 +0,0 @@ -# Compute Resources - -Functions and classes that execute serverless workloads in Modal's cloud infrastructure, including interactive sandboxes for development and debugging. - -## Capabilities - -### Function Class - -Represents a deployed serverless function that can be called remotely, locally, or in parallel across multiple inputs. - -```python { .api } -class Function: - def remote(self, *args, **kwargs): - """ - Call the function remotely in Modal's cloud. - - Parameters: - - *args: Positional arguments to pass to the function - - **kwargs: Keyword arguments to pass to the function - - Returns: - Result of the function execution - """ - - def local(self, *args, **kwargs): - """ - Call the function locally (useful for testing). - - Parameters: - - *args: Positional arguments to pass to the function - - **kwargs: Keyword arguments to pass to the function - - Returns: - Result of the function execution - """ - - def map( - self, - inputs, - order_outputs: bool = True, - return_exceptions: bool = False - ): - """ - Map the function over a list of inputs in parallel. - - Parameters: - - inputs: Iterable of inputs to map over - - order_outputs: Whether to return outputs in input order - - return_exceptions: Include exceptions in results instead of raising - - Returns: - List of results - """ - - def spawn(self, *args, **kwargs): - """ - Spawn the function asynchronously without waiting for result. - - Parameters: - - *args: Positional arguments to pass to the function - - **kwargs: Keyword arguments to pass to the function - - Returns: - FunctionCall object for retrieving result later - """ - - @classmethod - def from_name( - cls, - app_name: str, - tag: str = None, - namespace: str = None, - environment_name: str = None - ): - """ - Look up a deployed function by name. - - Parameters: - - app_name: Name of the app containing the function - - tag: Optional deployment tag - - namespace: Namespace to search in - - environment_name: Environment name - - Returns: - Function object - """ -``` - -### FunctionCall Class - -Represents an executing or executed function call, providing methods to retrieve results and manage execution. - -```python { .api } -class FunctionCall: - def get(self, timeout: float = None): - """ - Get the result of the function call, blocking until completion. - - Parameters: - - timeout: Maximum time to wait for result in seconds - - Returns: - Result of the function call - - Raises: - TimeoutError: If timeout is exceeded - """ - - def cancel(self): - """ - Cancel the running function call. - - Returns: - True if cancellation was successful - """ - - def is_finished(self) -> bool: - """ - Check if the function call has completed. - - Returns: - True if finished, False otherwise - """ - - @property - def object_id(self) -> str: - """ - Get the unique ID of this function call. - - Returns: - Function call ID string - """ -``` - -### Cls Class - -Serverless class for stateful compute with lifecycle methods, enabling persistent state across method calls. - -```python { .api } -class Cls: - @classmethod - def from_name( - cls, - app_name: str, - tag: str = None, - namespace: str = None, - environment_name: str = None - ): - """ - Look up a deployed class by name. - - Parameters: - - app_name: Name of the app containing the class - - tag: Optional deployment tag - - namespace: Namespace to search in - - environment_name: Environment name - - Returns: - Cls object - """ - - def lookup(self, method_name: str): - """ - Look up a specific method of the class. - - Parameters: - - method_name: Name of the method to look up - - Returns: - Method object that can be called remotely - """ -``` - -### Sandbox Class - -Interactive compute environment for development, debugging, and ad-hoc computation. - -```python { .api } -class Sandbox: - @classmethod - def create( - cls, - image: Image = None, - secrets: list[Secret] = None, - volumes: dict[str, Volume] = None, - mounts: list[Mount] = None, - memory: int = None, - cpu: float = None, - gpu: GPU_T = None, - timeout: int = None, - workdir: str = "/root", - **kwargs - ): - """ - Create a new interactive sandbox. - - Parameters: - - image: Container image for the sandbox - - secrets: List of secrets to inject - - volumes: Dictionary mapping paths to volumes - - mounts: List of mount objects - - memory: Memory limit in MB - - cpu: CPU allocation - - gpu: GPU configuration - - timeout: Sandbox timeout in seconds - - workdir: Working directory - - Returns: - Sandbox instance - """ - - def exec( - self, - command: str, - workdir: str = None, - secrets: list[Secret] = None, - **kwargs - ): - """ - Execute a command in the sandbox. - - Parameters: - - command: Shell command to execute - - workdir: Working directory for command - - secrets: Additional secrets for this command - - Returns: - Command execution result - """ - - def terminate(self): - """ - Terminate the sandbox, cleaning up all resources. - """ - - def tunnel(self, port: int): - """ - Create a tunnel to a port in the sandbox. - - Parameters: - - port: Port number to tunnel to - - Returns: - Tunnel URL for accessing the port - """ - - @property - def object_id(self) -> str: - """ - Get the unique ID of this sandbox. - - Returns: - Sandbox ID string - """ -``` - -### SandboxSnapshot Class - -Snapshot of a sandbox environment that can be reused to create new sandboxes or deploy as container images. - -```python { .api } -class SandboxSnapshot: - @classmethod - def create( - cls, - sandbox: Sandbox, - label: str = None - ): - """ - Create a snapshot from an existing sandbox. - - Parameters: - - sandbox: Source sandbox to snapshot - - label: Optional label for the snapshot - - Returns: - SandboxSnapshot instance - """ - - def deploy(self, name: str): - """ - Deploy the snapshot as a container image. - - Parameters: - - name: Name for the deployed image - - Returns: - Deployed image reference - """ - - @classmethod - def from_name( - cls, - label: str, - namespace: str = None - ): - """ - Look up a snapshot by label. - - Parameters: - - label: Snapshot label - - namespace: Optional namespace - - Returns: - SandboxSnapshot instance - """ -``` - -## Usage Examples - -### Basic Function Usage - -```python -import modal - -app = modal.App("compute-example") - -@app.function() -def fibonacci(n: int) -> int: - if n <= 1: - return n - return fibonacci(n-1) + fibonacci(n-2) - -@app.local_entrypoint() -def main(): - # Call function remotely - result = fibonacci.remote(10) - print(f"Fibonacci(10) = {result}") - - # Map over multiple inputs - results = fibonacci.map([1, 2, 3, 4, 5]) - print(f"Results: {results}") - - # Spawn async call - call = fibonacci.spawn(15) - # Do other work... - result = call.get() - print(f"Async result: {result}") -``` - -### Stateful Class Example - -```python -import modal -from modal import enter, exit, method - -app = modal.App("stateful-service") - -@app.cls() -class DatabaseService: - @enter() - def setup(self): - # Initialize database connection - self.connection = create_db_connection() - self.cache = {} - - @exit() - def cleanup(self): - # Clean up resources - self.connection.close() - - @method() - def query(self, sql: str): - if sql in self.cache: - return self.cache[sql] - - result = self.connection.execute(sql) - self.cache[sql] = result - return result - - @method() - def insert(self, table: str, data: dict): - return self.connection.insert(table, data) - -@app.local_entrypoint() -def main(): - db = DatabaseService() - - # Insert data - db.insert.remote("users", {"name": "Alice", "email": "alice@example.com"}) - - # Query data (uses caching) - users = db.query.remote("SELECT * FROM users") - print(users) -``` - -### Interactive Sandbox - -```python -import modal - -# Create a sandbox with custom environment -sandbox = modal.Sandbox.create( - image=modal.Image.debian_slim().pip_install("pandas", "matplotlib"), - timeout=3600 # 1 hour -) - -# Execute commands -result = sandbox.exec("python -c 'import pandas; print(pandas.__version__)'") -print(f"Pandas version: {result.stdout}") - -# Run a data analysis script -analysis_code = """ -import pandas as pd -import matplotlib.pyplot as plt - -# Create sample data -data = {'x': range(10), 'y': [i**2 for i in range(10)]} -df = pd.DataFrame(data) - -# Create plot -plt.figure(figsize=(8, 6)) -plt.plot(df['x'], df['y']) -plt.savefig('/tmp/plot.png') -plt.close() - -print("Analysis complete") -""" - -result = sandbox.exec(f"python -c \"{analysis_code}\"") -print(result.stdout) - -# Terminate when done -sandbox.terminate() -``` - -### Sandbox Snapshots - -```python -import modal - -# Create and configure a sandbox -sandbox = modal.Sandbox.create( - image=modal.Image.debian_slim() -) - -# Install custom software -sandbox.exec("apt-get update && apt-get install -y git curl") -sandbox.exec("pip install numpy scipy scikit-learn") - -# Create a snapshot -snapshot = modal.SandboxSnapshot.create(sandbox, label="ml-environment") - -# Use the snapshot to create new sandboxes -new_sandbox = modal.Sandbox.create(image=snapshot) - -# Or deploy as an image for functions -app = modal.App("ml-app") - -@app.function(image=snapshot) -def ml_computation(data): - import numpy as np - from sklearn.linear_model import LinearRegression - - # ML computation using pre-installed libraries - model = LinearRegression() - return model.fit(data['X'], data['y']).predict(data['X_test']) -``` - -### Parallel Processing - -```python -import modal - -app = modal.App("parallel-processing") - -@app.function() -def process_batch(batch_data: list) -> dict: - # Process a batch of data - results = {} - for item in batch_data: - results[item['id']] = expensive_computation(item['data']) - return results - -@app.local_entrypoint() -def main(): - # Prepare data batches - all_data = [{'id': i, 'data': f"data_{i}"} for i in range(1000)] - batches = [all_data[i:i+100] for i in range(0, len(all_data), 100)] - - # Process batches in parallel - batch_results = process_batch.map(batches) - - # Combine results - final_results = {} - for batch_result in batch_results: - final_results.update(batch_result) - - print(f"Processed {len(final_results)} items") -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/container-images.md b/.tessl/tiles/tessl/pypi-modal/docs/container-images.md deleted file mode 100644 index b6127fecc..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/container-images.md +++ /dev/null @@ -1,503 +0,0 @@ -# Container Images - -Container image definitions that specify the runtime environment for functions and classes. Images define the operating system, Python version, packages, and system dependencies needed for your code to run. - -## Capabilities - -### Image Class - -Defines container environments with pre-installed dependencies, custom configurations, and optimized builds for serverless execution. - -```python { .api } -class Image: - @classmethod - def debian_slim( - cls, - python_version: str = None, - builder_version: str = None - ): - """ - Create a Debian slim base image with Python. - - Parameters: - - python_version: Python version (e.g., "3.11", "3.12"). Defaults to local version. - - builder_version: Image builder version for reproducible builds - - Returns: - Image instance with Debian slim base - """ - - @classmethod - def from_registry( - cls, - tag: str, - secret: Secret = None, - setup_dockerfile_commands: list[str] = None - ): - """ - Create an image from a container registry (Docker Hub, ECR, etc.). - - Parameters: - - tag: Full image tag (e.g., "python:3.11-slim", "ubuntu:22.04") - - secret: Secret for private registry authentication - - setup_dockerfile_commands: Additional setup commands to run - - Returns: - Image instance based on registry image - """ - - def pip_install( - self, - *packages: str, - find_links: str = None, - index_url: str = None, - extra_index_url: str = None, - pre: bool = False, - force_build: bool = False - ): - """ - Install Python packages using pip. - - Parameters: - - *packages: Package names, optionally with version specifiers - - find_links: Additional URLs to search for packages - - index_url: Base URL of package index - - extra_index_url: Extra URLs of package indexes - - pre: Include pre-release versions - - force_build: Force rebuild even if cached - - Returns: - New Image instance with packages installed - """ - - def poetry_install_from_file( - self, - poetry_pyproject_toml: str, - poetry_lock_file: str = None, - ignore_lockfile: bool = False - ): - """ - Install dependencies from Poetry configuration files. - - Parameters: - - poetry_pyproject_toml: Path to pyproject.toml file - - poetry_lock_file: Path to poetry.lock file - - ignore_lockfile: Install without using lockfile - - Returns: - New Image instance with Poetry dependencies - """ - - def run_commands( - self, - *commands: str, - secrets: list[Secret] = None, - gpu: str = None - ): - """ - Run shell commands during image build. - - Parameters: - - *commands: Shell commands to execute - - secrets: Secrets available during build - - gpu: GPU type for build environment - - Returns: - New Image instance with commands executed - """ - - def apt_install( - self, - *packages: str, - force_build: bool = False - ): - """ - Install system packages using apt (Debian/Ubuntu). - - Parameters: - - *packages: Package names to install - - force_build: Force rebuild even if cached - - Returns: - New Image instance with system packages - """ - - def copy_local_file( - self, - local_path: str, - remote_path: str = None - ): - """ - Copy a local file into the image. - - Parameters: - - local_path: Path to local file or directory - - remote_path: Destination path in image. Defaults to same path. - - Returns: - New Image instance with file copied - """ - - def copy_local_dir( - self, - local_path: str, - remote_path: str = None, - condition: callable = None - ): - """ - Copy a local directory into the image. - - Parameters: - - local_path: Path to local directory - - remote_path: Destination path in image - - condition: Function to filter which files to copy - - Returns: - New Image instance with directory copied - """ - - def add_python_packages( - self, - *python_packages: str, - force_build: bool = False - ): - """ - Add Python packages from local wheel files or directories. - - Parameters: - - *python_packages: Paths to wheel files or package directories - - force_build: Force rebuild even if cached - - Returns: - New Image instance with packages added - """ - - def dockerfile_commands( - self, - dockerfile_commands: list[str], - secrets: list[Secret] = None, - gpu: str = None, - context_files: dict = None - ): - """ - Execute Dockerfile commands during build. - - Parameters: - - dockerfile_commands: List of Dockerfile commands (RUN, COPY, etc.) - - secrets: Build secrets - - gpu: GPU type for build - - context_files: Files to include in build context - - Returns: - New Image instance with Dockerfile commands applied - """ - - def env(self, **env_vars: str): - """ - Set environment variables in the image. - - Parameters: - - **env_vars: Environment variables as keyword arguments - - Returns: - New Image instance with environment variables set - """ - - def workdir(self, path: str): - """ - Set the working directory for the image. - - Parameters: - - path: Working directory path - - Returns: - New Image instance with working directory set - """ -``` - -## Usage Examples - -### Basic Python Environment - -```python -import modal - -# Simple Python environment -image = modal.Image.debian_slim(python_version="3.11").pip_install( - "requests", - "beautifulsoup4", - "pandas==2.0.0" -) - -app = modal.App("web-scraper") - -@app.function(image=image) -def scrape_and_analyze(url: str): - import requests - import pandas as pd - from bs4 import BeautifulSoup - - response = requests.get(url) - soup = BeautifulSoup(response.content, 'html.parser') - - # Extract and analyze data - links = [a.get('href') for a in soup.find_all('a', href=True)] - df = pd.DataFrame({'links': links}) - - return { - 'total_links': len(links), - 'unique_domains': len(df['links'].apply(lambda x: x.split('/')[2] if x.startswith('http') else None).dropna().unique()) - } -``` - -### Data Science Environment - -```python -import modal - -# Comprehensive data science environment -ds_image = ( - modal.Image.debian_slim(python_version="3.11") - .apt_install("git", "curl", "build-essential") - .pip_install( - "numpy", - "pandas", - "scikit-learn", - "matplotlib", - "seaborn", - "jupyter", - "plotly", - "scipy" - ) - .run_commands( - "pip install --no-deps lightgbm", # Custom installation - "mkdir -p /opt/data" - ) - .env( - DATA_PATH="/opt/data", - PYTHONPATH="/opt/models" - ) -) - -app = modal.App("ml-pipeline") - -@app.function(image=ds_image) -def train_model(dataset_path: str): - import pandas as pd - from sklearn.ensemble import RandomForestClassifier - from sklearn.model_selection import train_test_split - from sklearn.metrics import accuracy_score - - # Load and prepare data - df = pd.read_csv(dataset_path) - X = df.drop('target', axis=1) - y = df['target'] - - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) - - # Train model - model = RandomForestClassifier(n_estimators=100) - model.fit(X_train, y_train) - - # Evaluate - predictions = model.predict(X_test) - accuracy = accuracy_score(y_test, predictions) - - return {"accuracy": accuracy, "model_type": "RandomForest"} -``` - -### Custom Base Image - -```python -import modal - -# Using a custom base image from Docker Hub -custom_image = ( - modal.Image.from_registry("nvidia/cuda:11.8-devel-ubuntu22.04") - .apt_install("python3", "python3-pip", "python3-dev") - .run_commands( - "ln -s /usr/bin/python3 /usr/bin/python", - "pip install --upgrade pip" - ) - .pip_install("torch", "torchvision", "transformers") - .env(CUDA_VISIBLE_DEVICES="0") -) - -app = modal.App("gpu-ml") - -@app.function( - image=custom_image, - gpu="T4" # GPU required for this function -) -def run_inference(model_name: str, input_text: str): - import torch - from transformers import AutoTokenizer, AutoModel - - # Load model and tokenizer - tokenizer = AutoTokenizer.from_pretrained(model_name) - model = AutoModel.from_pretrained(model_name) - - # Tokenize and run inference - inputs = tokenizer(input_text, return_tensors="pt") - with torch.no_grad(): - outputs = model(**inputs) - - return { - "embeddings_shape": outputs.last_hidden_state.shape, - "mean_embedding": outputs.last_hidden_state.mean().item() - } -``` - -### Development Environment with Local Code - -```python -import modal - -# Development image with local code and dependencies -dev_image = ( - modal.Image.debian_slim() - .pip_install("fastapi", "uvicorn", "sqlalchemy", "pytest") - .copy_local_dir("./src", "/app/src") # Copy local source code - .copy_local_file("requirements.txt", "/app/requirements.txt") - .run_commands("pip install -r /app/requirements.txt") - .workdir("/app") -) - -app = modal.App("development") - -@app.function(image=dev_image) -def run_tests(): - import subprocess - - # Run tests using the copied code - result = subprocess.run( - ["python", "-m", "pytest", "src/tests/", "-v"], - capture_output=True, - text=True, - cwd="/app" - ) - - return { - "exit_code": result.returncode, - "stdout": result.stdout, - "stderr": result.stderr - } -``` - -### Poetry-based Project - -```python -import modal - -# Using Poetry for dependency management -poetry_image = ( - modal.Image.debian_slim() - .apt_install("curl") - .run_commands( - "curl -sSL https://install.python-poetry.org | python3 -", - "export PATH=\"/root/.local/bin:$PATH\"" - ) - .poetry_install_from_file( - "pyproject.toml", - "poetry.lock" - ) - .copy_local_dir("./my_package", "/app/my_package") - .workdir("/app") -) - -app = modal.App("poetry-app") - -@app.function(image=poetry_image) -def process_with_poetry_deps(): - # Use dependencies installed via Poetry - from my_package import main_module - return main_module.process_data() -``` - -### Multi-stage Build - -```python -import modal - -# Complex multi-stage build with custom compilation -build_image = ( - modal.Image.debian_slim() - .apt_install( - "build-essential", - "cmake", - "git", - "pkg-config", - "libssl-dev" - ) - .run_commands( - # Clone and build custom library - "git clone https://github.com/example/custom-lib.git /tmp/custom-lib", - "cd /tmp/custom-lib && mkdir build && cd build", - "cmake .. && make -j$(nproc) && make install" - ) - .pip_install("numpy", "cython") - .copy_local_file("setup.py", "/app/setup.py") - .copy_local_dir("./src", "/app/src") - .run_commands( - "cd /app && python setup.py build_ext --inplace", - "pip install -e ." - ) - .apt_install("--purge", "build-essential", "cmake") # Clean up build tools - .run_commands("apt-get autoremove -y && apt-get clean") -) - -app = modal.App("optimized-build") - -@app.function(image=build_image) -def run_optimized_computation(data): - # Use the custom-built optimized library - import my_optimized_package - return my_optimized_package.fast_compute(data) -``` - -## Common Patterns - -### Environment Variables and Configuration - -```python -config_image = ( - modal.Image.debian_slim() - .pip_install("python-dotenv", "pydantic") - .env( - ENVIRONMENT="production", - LOG_LEVEL="INFO", - API_TIMEOUT="30" - ) - .copy_local_file(".env.production", "/app/.env") -) -``` - -### Caching for Faster Builds - -```python -# Use force_build=False (default) to enable caching -cached_image = ( - modal.Image.debian_slim() - .pip_install("numpy", "pandas", force_build=False) # Will be cached - .copy_local_file("requirements.txt") # This will trigger rebuild if file changes - .pip_install("-r", "requirements.txt") -) -``` - -### Secrets in Build Process - -```python -build_secret = modal.Secret.from_dict({ - "GITHUB_TOKEN": "ghp_...", - "PRIVATE_REGISTRY_TOKEN": "..." -}) - -secure_image = ( - modal.Image.debian_slim() - .run_commands( - "git clone https://${GITHUB_TOKEN}@github.com/private/repo.git /app", - secrets=[build_secret] - ) - .pip_install("--extra-index-url", "https://token:${PRIVATE_REGISTRY_TOKEN}@pypi.private.com/simple/", "private-package", secrets=[build_secret]) -) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/core-application-client.md b/.tessl/tiles/tessl/pypi-modal/docs/core-application-client.md deleted file mode 100644 index 34a81170a..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/core-application-client.md +++ /dev/null @@ -1,284 +0,0 @@ -# Core Application & Client - -Primary interfaces for defining applications and managing authentication and object lookup with Modal's cloud platform. - -## Capabilities - -### App Class - -Main application container for defining and deploying Modal functions and resources. All serverless functions, classes, and resources are defined within the context of an App. - -```python { .api } -class App: - def __init__(self, name: str = None): - """ - Create a new Modal application. - - Parameters: - - name: Optional name for the app. If not provided, a name will be generated. - """ - - def function( - self, - image: Optional["Image"] = None, - schedule: Optional[Union["Cron", "Period"]] = None, - secrets: Optional[list["Secret"]] = None, - volumes: Optional[dict[str, "Volume"]] = None, - network_file_systems: Optional[dict[str, "NetworkFileSystem"]] = None, - cloud_bucket_mounts: Optional[dict[str, "CloudBucketMount"]] = None, - memory: Optional[int] = None, - cpu: Optional[float] = None, - gpu: Optional[str] = None, - timeout: Optional[int] = None, - retries: Optional[Union[int, "Retries"]] = None, - concurrency_limit: Optional[int] = None, - allow_concurrent_inputs: Optional[int] = None, - container_idle_timeout: Optional[int] = None, - keep_warm: Optional[int] = None, - **kwargs - ): - """ - Decorator to define a serverless function within the app. - - Parameters: - - image: Container image to run the function in - - schedule: Schedule for automatic execution (Cron or Period) - - secrets: List of secrets to inject as environment variables - - volumes: Dictionary mapping mount paths to Volume objects - - network_file_systems: Dictionary mapping mount paths to NetworkFileSystem objects - - cloud_bucket_mounts: Dictionary mapping mount paths to CloudBucketMount objects - - memory: Memory limit in MB - - cpu: CPU allocation (fractional values supported) - - gpu: GPU specification string (e.g., "any", "a10g", "h100") - - timeout: Function timeout in seconds - - retries: Retry policy for failed executions - - concurrency_limit: Maximum concurrent executions - - allow_concurrent_inputs: Allow concurrent inputs per container - - container_idle_timeout: How long to keep containers warm - - keep_warm: Number of containers to keep warm - - Returns: - Function decorator - """ - - def cls( - self, - image: Image = None, - secrets: list[Secret] = None, - volumes: dict[str, Volume] = None, - mounts: list[Mount] = None, - memory: int = None, - cpu: float = None, - gpu: GPU_T = None, - timeout: int = None, - retries: Retries = None, - concurrency_limit: int = None, - container_idle_timeout: int = None, - keep_warm: int = None, - **kwargs - ): - """ - Decorator to define a serverless class within the app. - - Parameters: - - image: Container image to run the class in - - secrets: List of secrets to inject as environment variables - - volumes: Dictionary mapping mount paths to Volume objects - - mounts: List of mount objects for code and data - - memory: Memory limit in MB - - cpu: CPU allocation - - gpu: GPU configuration - - timeout: Method timeout in seconds - - retries: Retry policy for failed method calls - - concurrency_limit: Maximum concurrent class instances - - container_idle_timeout: How long to keep containers warm - - keep_warm: Number of containers to keep warm - - Returns: - Class decorator - """ - - def local_entrypoint(self): - """ - Decorator to define a local entry point that can call remote functions. - - The decorated function will run locally and can invoke remote functions - defined in the same app. - - Returns: - Function decorator - """ - - def deploy(self, name: str = None): - """ - Deploy the app to Modal cloud. - - Parameters: - - name: Optional deployment name - - Returns: - Deployed app handle - """ - - def run(self, detach: bool = False): - """ - Run the app, executing the local entrypoint if defined. - - Parameters: - - detach: Run in detached mode - """ - - def stop(self): - """ - Stop a running app. - """ - - def list_objects(self): - """ - List all objects (functions, classes, etc.) defined in the app. - - Returns: - List of app objects - """ -``` - -### Client Class - -Client for interacting with Modal's API, managing authentication, and looking up deployed objects. - -```python { .api } -class Client: - @classmethod - def from_env( - cls, - profile: str = None, - token_id: str = None, - token_secret: str = None - ): - """ - Create a client from environment variables or profile. - - Parameters: - - profile: Named profile to use for authentication - - token_id: Override token ID from environment - - token_secret: Override token secret from environment - - Returns: - Authenticated Client instance - """ - - def lookup( - self, - label: str, - namespace: str = None, - create_if_missing: bool = False - ): - """ - Look up a deployed object by label. - - Parameters: - - label: Label of the object to look up - - namespace: Namespace to search in - - create_if_missing: Create the object if it doesn't exist - - Returns: - The deployed object - """ - - def list(self, namespace: str = None): - """ - List objects in the account. - - Parameters: - - namespace: Optional namespace to filter by - - Returns: - List of deployed objects - """ -``` - -## Usage Examples - -### Basic App Definition - -```python -import modal - -# Create an app -app = modal.App("my-application") - -# Define a simple function -@app.function() -def hello(name: str) -> str: - return f"Hello, {name}!" - -# Local entrypoint to test the function -@app.local_entrypoint() -def main(): - result = hello.remote("World") - print(result) # "Hello, World!" -``` - -### App with Custom Configuration - -```python -import modal - -app = modal.App("data-processor") - -# Custom image with dependencies -image = modal.Image.debian_slim().pip_install("pandas", "numpy") - -# Function with resource configuration -@app.function( - image=image, - memory=2048, # 2GB memory - timeout=600, # 10 minute timeout - secrets=[modal.Secret.from_name("api-key")], - volumes={"/data": modal.Volume.from_name("dataset")} -) -def process_data(filename: str): - import pandas as pd - df = pd.read_csv(f"/data/{filename}") - return df.groupby('category').sum().to_dict() - -@app.local_entrypoint() -def main(): - result = process_data.remote("sales_data.csv") - print(result) -``` - -### Client Usage - -```python -import modal - -# Create client from environment -client = modal.Client.from_env() - -# Look up a deployed function -my_function = client.lookup("my-function") - -# Call the remote function -result = my_function.remote("input_data") -``` - -### Deployment - -```python -import modal - -app = modal.App("production-app") - -@app.function() -def my_function(): - return "Hello from production!" - -# Deploy to Modal cloud -if __name__ == "__main__": - # Deploy the app - app.deploy("v1.0") - - # Or run locally for development - # app.run() -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md b/.tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md deleted file mode 100644 index f49bff702..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/function-decorators-helpers.md +++ /dev/null @@ -1,418 +0,0 @@ -# Function Decorators & Helpers - -Modal provides specialized decorators and helper functions for enhancing function behavior, defining class lifecycle methods, enabling batched execution, and controlling concurrency. These tools allow fine-grained control over how functions execute in the Modal environment. - -## Capabilities - -### Method Decorator - -Decorator for defining methods within Modal classes, enabling stateful serverless computing with shared instance state. - -```python { .api } -def method(func: Callable) -> Callable: - """Decorator to define methods within Modal classes""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -@app.cls() -class DataProcessor: - def __init__(self, model_path: str): - # Constructor runs during instance creation - self.model = load_model(model_path) - self.cache = {} - - @modal.method() - def process_single(self, data: str) -> str: - # Method can access instance state - if data in self.cache: - return self.cache[data] - - result = self.model.predict(data) - self.cache[data] = result - return result - - @modal.method() - def process_batch(self, data_list: list[str]) -> list[str]: - # Another method sharing the same instance state - return [self.process_single(data) for data in data_list] - - @modal.method() - def get_cache_size(self) -> int: - return len(self.cache) - -# Usage -@app.local_entrypoint() -def main(): - processor = DataProcessor("path/to/model") - - # Call methods on the remote instance - result1 = processor.process_single.remote("input1") - result2 = processor.process_batch.remote(["input2", "input3"]) - cache_size = processor.get_cache_size.remote() - - print(f"Results: {result1}, {result2}") - print(f"Cache size: {cache_size}") -``` - -### Parameter Helper - -Helper function for defining class initialization parameters with validation and default values, similar to dataclass fields. - -```python { .api } -def parameter(*, default: Any = _no_default, init: bool = True) -> Any: - """Define class initialization parameters with options""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -@app.cls() -class ConfigurableService: - # Parameters with type annotations and defaults - model_name: str = modal.parameter() - batch_size: int = modal.parameter(default=32) - temperature: float = modal.parameter(default=0.7) - debug_mode: bool = modal.parameter(default=False) - - # Internal field not used in constructor - _internal_cache: dict = modal.parameter(init=False) - - def __post_init__(self): - # Initialize internal state after parameter injection - self._internal_cache = {} - print(f"Service initialized with model={self.model_name}, batch_size={self.batch_size}") - - @modal.method() - def configure_service(self): - # Use parameters in methods - if self.debug_mode: - print(f"Debug: Processing with temperature={self.temperature}") - - return { - "model": self.model_name, - "batch_size": self.batch_size, - "temperature": self.temperature - } - -# Usage with different configurations -@app.local_entrypoint() -def main(): - # Create instances with different parameters - service1 = ConfigurableService("gpt-4", batch_size=64, debug_mode=True) - service2 = ConfigurableService("claude-3", temperature=0.5) - - config1 = service1.configure_service.remote() - config2 = service2.configure_service.remote() - - print("Service 1 config:", config1) - print("Service 2 config:", config2) -``` - -### Lifecycle Decorators - -Decorators for defining class lifecycle methods that run during container startup and shutdown. - -```python { .api } -def enter(func: Callable) -> Callable: - """Decorator for class enter lifecycle method (runs on container startup)""" - -def exit(func: Callable) -> Callable: - """Decorator for class exit lifecycle method (runs on container shutdown)""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -@app.cls() -class DatabaseService: - def __init__(self, connection_string: str): - self.connection_string = connection_string - self.connection = None - self.cache = None - - @modal.enter() - def setup_connections(self): - """Run once when container starts""" - print("Setting up database connection...") - self.connection = create_database_connection(self.connection_string) - self.cache = initialize_cache() - print("Database service ready!") - - @modal.exit() - def cleanup_connections(self): - """Run once when container shuts down""" - print("Cleaning up database connections...") - if self.connection: - self.connection.close() - if self.cache: - self.cache.clear() - print("Cleanup complete!") - - @modal.method() - def query_data(self, sql: str) -> list[dict]: - # Connection is already established from enter() - cursor = self.connection.cursor() - cursor.execute(sql) - return cursor.fetchall() - - @modal.method() - def cached_query(self, sql: str) -> list[dict]: - # Use cache initialized in enter() - if sql in self.cache: - return self.cache[sql] - - result = self.query_data(sql) - self.cache[sql] = result - return result - -# Usage -@app.local_entrypoint() -def main(): - db_service = DatabaseService("postgresql://user:pass@host:5432/db") - - # First call triggers enter() lifecycle - results = db_service.query_data.remote("SELECT * FROM users LIMIT 10") - - # Subsequent calls reuse the established connection - cached_results = db_service.cached_query.remote("SELECT COUNT(*) FROM users") - - print("Query results:", results) - print("Cached results:", cached_results) - - # Container shutdown triggers exit() lifecycle automatically -``` - -### Execution Control Decorators - -Decorators for controlling how functions execute, including batching and concurrency patterns. - -```python { .api } -def batched(max_batch_size: int = 10) -> Callable: - """Decorator to enable batched function calls for improved throughput""" - -def concurrent(func: Callable) -> Callable: - """Decorator to enable concurrent function execution""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -@app.function() -@modal.batched(max_batch_size=50) -def process_items_batched(items: list[str]) -> list[str]: - """Process multiple items in a single function call""" - print(f"Processing batch of {len(items)} items") - - # Expensive setup that benefits from batching - model = load_expensive_model() - - # Process all items in the batch - results = [] - for item in items: - result = model.process(item) - results.append(result) - - return results - -@app.function() -@modal.concurrent -def process_item_concurrent(item: str) -> str: - """Process items with concurrent execution""" - # Each call can run concurrently with others - return expensive_processing(item) - -@app.local_entrypoint() -def main(): - # Batched processing - items are automatically grouped - items = [f"item_{i}" for i in range(100)] - - # These calls will be automatically batched up to max_batch_size - batch_results = [] - for item in items: - result = process_items_batched.remote([item]) # Each call adds to batch - batch_results.append(result) - - print(f"Batched processing completed: {len(batch_results)} results") - - # Concurrent processing - items run in parallel - concurrent_futures = [] - for item in items[:10]: # Process first 10 concurrently - future = process_item_concurrent.spawn(item) - concurrent_futures.append(future) - - # Collect concurrent results - concurrent_results = [future.get() for future in concurrent_futures] - print(f"Concurrent processing completed: {len(concurrent_results)} results") -``` - -## Advanced Patterns - -### Stateful Service with Lifecycle Management - -```python -import modal - -app = modal.App() - -@app.cls() -class MLInferenceService: - model_name: str = modal.parameter() - cache_size: int = modal.parameter(default=1000) - - @modal.enter() - def load_model(self): - """Load model and initialize cache on container start""" - print(f"Loading model: {self.model_name}") - self.model = download_and_load_model(self.model_name) - self.prediction_cache = LRUCache(maxsize=self.cache_size) - self.stats = {"requests": 0, "cache_hits": 0} - print("Model loaded and ready for inference") - - @modal.exit() - def save_stats(self): - """Save statistics before container shutdown""" - print(f"Final stats: {self.stats}") - save_stats_to_database(self.stats) - - @modal.method() - @modal.batched(max_batch_size=32) - def predict_batch(self, inputs: list[str]) -> list[dict]: - """Batched prediction with caching""" - results = [] - uncached_inputs = [] - uncached_indices = [] - - # Check cache for each input - for i, inp in enumerate(inputs): - if inp in self.prediction_cache: - results.append(self.prediction_cache[inp]) - self.stats["cache_hits"] += 1 - else: - results.append(None) # Placeholder - uncached_inputs.append(inp) - uncached_indices.append(i) - - # Batch process uncached inputs - if uncached_inputs: - batch_predictions = self.model.predict(uncached_inputs) - for idx, prediction in zip(uncached_indices, batch_predictions): - self.prediction_cache[inputs[idx]] = prediction - results[idx] = prediction - - self.stats["requests"] += len(inputs) - return results - - @modal.method() - def get_stats(self) -> dict: - """Get current service statistics""" - return self.stats.copy() - -# Usage -@app.local_entrypoint() -def main(): - # Create service instance - ml_service = MLInferenceService(model_name="bert-base-uncased", cache_size=500) - - # Make predictions (automatically batched) - test_inputs = [f"test sentence {i}" for i in range(100)] - predictions = ml_service.predict_batch.remote(test_inputs) - - # Check service statistics - stats = ml_service.get_stats.remote() - print(f"Service stats: {stats}") - - # Make some repeated predictions to test caching - repeat_predictions = ml_service.predict_batch.remote(test_inputs[:10]) - final_stats = ml_service.get_stats.remote() - print(f"Final stats with cache hits: {final_stats}") -``` - -### Concurrent Task Processing with Shared State - -```python -import modal - -app = modal.App() - -@app.cls() -class TaskProcessor: - max_workers: int = modal.parameter(default=10) - - @modal.enter() - def setup_processor(self): - """Initialize shared resources""" - self.task_queue = initialize_task_queue() - self.result_store = initialize_result_store() - self.worker_stats = {} - - @modal.method() - @modal.concurrent - def process_task_concurrent(self, task_id: str, worker_id: str) -> dict: - """Process individual tasks concurrently""" - # Track worker statistics - if worker_id not in self.worker_stats: - self.worker_stats[worker_id] = {"processed": 0, "errors": 0} - - try: - # Process the task - task_data = self.task_queue.get_task(task_id) - result = expensive_task_processing(task_data) - - # Store result - self.result_store.put(task_id, result) - self.worker_stats[worker_id]["processed"] += 1 - - return {"status": "success", "task_id": task_id, "worker": worker_id} - - except Exception as e: - self.worker_stats[worker_id]["errors"] += 1 - return {"status": "error", "task_id": task_id, "error": str(e)} - - @modal.method() - def get_worker_stats(self) -> dict: - """Get statistics for all workers""" - return self.worker_stats.copy() - -@app.local_entrypoint() -def main(): - processor = TaskProcessor(max_workers=20) - - # Process many tasks concurrently - task_ids = [f"task_{i}" for i in range(100)] - futures = [] - - for i, task_id in enumerate(task_ids): - worker_id = f"worker_{i % 20}" # Distribute across workers - future = processor.process_task_concurrent.spawn(task_id, worker_id) - futures.append(future) - - # Collect results - results = [future.get() for future in futures] - - # Check worker statistics - stats = processor.get_worker_stats.remote() - print(f"Worker statistics: {stats}") - - # Analyze results - successful = sum(1 for r in results if r["status"] == "success") - errors = sum(1 for r in results if r["status"] == "error") - print(f"Processed {successful} tasks successfully, {errors} errors") -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/index.md b/.tessl/tiles/tessl/pypi-modal/docs/index.md deleted file mode 100644 index 9ae30e179..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/index.md +++ /dev/null @@ -1,346 +0,0 @@ -# Modal - -Modal is a Python client library for Modal, a serverless cloud computing platform that enables developers to run Python code in the cloud with on-demand access to compute resources. The library provides a comprehensive toolkit for building, deploying, and managing serverless applications including functions, classes, containers, volumes, secrets, and file systems. It features a rich API for defining cloud resources declaratively, supports parallel execution and distributed computing patterns, includes built-in monitoring and logging capabilities, and provides seamless integration with popular Python frameworks and libraries. - -## Package Information - -- **Package Name**: modal -- **Language**: Python -- **Installation**: `pip install modal` -- **Python Requirements**: >= 3.9, < 3.14 - -## Core Imports - -```python -import modal -``` - -Common pattern for accessing main classes: - -```python -from modal import App, Image, Secret, Volume -``` - -Alternative import all public APIs: - -```python -from modal import * -``` - -## Basic Usage - -```python -import modal - -# Define an application -app = modal.App("my-app") - -# Define a custom container image with dependencies -image = modal.Image.debian_slim().pip_install("requests", "beautifulsoup4") - -# Define a serverless function -@app.function(image=image) -def scrape_url(url: str) -> str: - import requests - from bs4 import BeautifulSoup - - response = requests.get(url) - soup = BeautifulSoup(response.content, 'html.parser') - return soup.get_text() - -# Local entrypoint to run the function -@app.local_entrypoint() -def main(): - result = scrape_url.remote("https://example.com") - print(result) - -# Deploy the app -if __name__ == "__main__": - app.run() -``` - -## Architecture - -Modal follows a declarative resource model where applications are composed of: - -- **App**: Main container that groups functions, classes, and resources -- **Compute Resources**: Functions and Classes that execute in the cloud -- **Infrastructure**: Storage (Volumes, NetworkFileSystem), networking (Proxy, Tunnel), and security (Secret) -- **Images**: Container definitions that specify the runtime environment -- **Scheduling**: Cron jobs and periodic tasks for automated execution - -The library uses the synchronicity pattern to provide both synchronous and asynchronous APIs from a single codebase, enabling flexible usage patterns while maintaining type safety. - -## Capabilities - -### Core Application & Client - -Primary interfaces for defining applications and managing authentication with Modal's cloud platform. - -```python { .api } -class App: - def __init__(self, name: str): ... - def function(self, **kwargs): ... # Decorator - def cls(self, **kwargs): ... # Decorator - def local_entrypoint(self): ... # Decorator - def deploy(self): ... - def run(self): ... - -class Client: - @classmethod - def from_env(cls): ... - def lookup(self, label: str): ... -``` - -[Core Application & Client](./core-application-client.md) - -### Compute Resources - -Functions and classes that execute serverless workloads in Modal's cloud infrastructure. - -```python { .api } -class Function: - def remote(self, *args, **kwargs): ... - def local(self, *args, **kwargs): ... - def map(self, inputs): ... - def spawn(self, *args, **kwargs): ... - -class FunctionCall: - def get(self): ... - def cancel(self): ... - -class Cls: - @classmethod - def from_name(cls, label: str): ... - def lookup(self, name: str): ... - -class Sandbox: - @classmethod - def create(cls, **kwargs): ... - def exec(self, command: str): ... - def terminate(self): ... -``` - -[Compute Resources](./compute-resources.md) - -### Container Images - -Container image definitions that specify the runtime environment for functions and classes. - -```python { .api } -class Image: - @classmethod - def debian_slim(cls, python_version: str = None): ... - @classmethod - def from_registry(cls, tag: str): ... - def pip_install(self, *packages: str): ... - def run_commands(self, *commands: str): ... - def copy_local_file(self, local_path: str, remote_path: str): ... -``` - -[Container Images](./container-images.md) - -### Storage & Data - -Persistent storage solutions including volumes, network file systems, key-value stores, and cloud bucket mounts. - -```python { .api } -class Volume: - @classmethod - def from_name(cls, label: str): ... - @classmethod - def persist(cls, label: str): ... - -class NetworkFileSystem: - @classmethod - def from_name(cls, label: str): ... - @classmethod - def persist(cls, label: str): ... - -class Dict: - @classmethod - def from_name(cls, label: str): ... - def get(self, key: str): ... - def put(self, key: str, value): ... - def pop(self, key: str): ... - -class Queue: - @classmethod - def from_name(cls, label: str): ... - def put(self, item): ... - def get(self): ... -``` - -[Storage & Data](./storage-data.md) - -### Infrastructure Services - -Networking, security, and infrastructure services for cloud applications. - -```python { .api } -class Secret: - @classmethod - def from_name(cls, label: str): ... - @classmethod - def from_dict(cls, mapping: dict): ... - -class Proxy: - @classmethod - def from_name(cls, label: str): ... - -class Tunnel: - @classmethod - def create(cls, **kwargs): ... - -class SchedulerPlacement: - @classmethod - def zone(cls, zone: str): ... -``` - -[Infrastructure Services](./infrastructure-services.md) - -### Function Decorators & Helpers - -Decorators and helper functions for enhancing function behavior and defining lifecycle methods. - -```python { .api } -def method(func): ... -def parameter(name: str, default=None): ... -def enter(func): ... -def exit(func): ... -def batched(max_batch_size: int): ... -def concurrent(func): ... -``` - -[Function Decorators & Helpers](./function-decorators-helpers.md) - -### Web & API Integration - -Web application serving capabilities including ASGI, WSGI, and HTTP endpoint support. - -```python { .api } -def asgi_app(func): ... -def wsgi_app(func): ... -def web_endpoint(func): ... -def fastapi_endpoint(func): ... -def web_server(func): ... -``` - -[Web & API Integration](./web-api-integration.md) - -### Scheduling & Reliability - -Task scheduling and retry policies for automated and resilient execution. - -```python { .api } -class Cron: - def __init__(self, cron_string: str): ... - -class Period: - @classmethod - def days(cls, n: int): ... - @classmethod - def hours(cls, n: int): ... - @classmethod - def minutes(cls, n: int): ... - @classmethod - def seconds(cls, n: int): ... - -class Retries: - def __init__( - self, - max_retries: int = 3, - backoff: float = 2.0, - initial_delay: float = 1.0 - ): ... -``` - -[Scheduling & Reliability](./scheduling-reliability.md) - -### Runtime Utilities - -Utilities for runtime context, debugging, and execution control within Modal functions. - -```python { .api } -def current_function_call_id() -> str: ... -def current_input_id() -> str: ... -def is_local() -> bool: ... -def interact(): ... -def enable_output(): ... -def forward(**kwargs): ... -``` - -[Runtime Utilities](./runtime-utilities.md) - -### Utility Classes - -General utility classes for error handling, file pattern matching, and package information. - -```python { .api } -__version__: str # Package version - -class Error(Exception): ... - -class FilePatternMatcher: - def __init__(self, patterns: list): ... - def matches(self, path: str) -> bool: ... -``` - -[Utility Classes](./utility-classes.md) - -## Common Patterns - -### Function with Custom Environment - -```python -import modal - -app = modal.App() - -@app.function( - image=modal.Image.debian_slim().pip_install("numpy", "pandas"), - secrets=[modal.Secret.from_name("my-secret")], - volumes={"/data": modal.Volume.from_name("my-volume")} -) -def process_data(filename: str): - import pandas as pd - df = pd.read_csv(f"/data/{filename}") - return df.describe().to_dict() -``` - -### Scheduled Function - -```python -from modal import App, Cron - -app = App() - -@app.function(schedule=Cron("0 0 * * *")) # Daily at midnight -def daily_report(): - # Generate and send daily report - print("Running daily report...") -``` - -### Class with Lifecycle Methods - -```python -from modal import App, enter, exit, method - -app = App() - -@app.cls() -class MyService: - @enter() - def setup(self): - # Initialize resources - self.client = create_client() - - @exit() - def cleanup(self): - # Clean up resources - self.client.close() - - @method() - def process(self, data): - return self.client.process(data) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md b/.tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md deleted file mode 100644 index 90bbf2f66..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/infrastructure-services.md +++ /dev/null @@ -1,389 +0,0 @@ -# Infrastructure Services - -Modal provides essential infrastructure services for networking, security, and resource management. These services enable secure communication, secret management, network connectivity, and fine-grained control over where functions execute. - -## Capabilities - -### Secret - Secure Environment Variable Management - -Secure storage and injection of environment variables, API keys, database credentials, and other sensitive data into Modal functions. - -```python { .api } -class Secret: - @classmethod - def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Secret": - """Load a Secret by its unique name""" - - @classmethod - def from_dict( - cls, - env_dict: dict[str, Optional[str]], - *, - name: Optional[str] = None - ) -> "Secret": - """Create a Secret from a dictionary of environment variables""" - - @classmethod - def from_dotenv(cls, path: Optional[str] = None) -> "Secret": - """Create a Secret from a .env file""" - -class SecretInfo: - """Information about a Secret object""" - name: Optional[str] - created_at: datetime - created_by: Optional[str] -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -# Create secret from dictionary -db_secret = modal.Secret.from_dict({ - "DATABASE_URL": "postgresql://user:pass@host:5432/db", - "DATABASE_PASSWORD": "secure_password", - "API_KEY": "your_api_key_here" -}) - -# Load existing secret by name -api_secret = modal.Secret.from_name("openai-api-key") - -@app.function(secrets=[db_secret, api_secret]) -def database_operation(): - import os - - # Access secret values as environment variables - db_url = os.environ["DATABASE_URL"] - api_key = os.environ["API_KEY"] - - # Use secrets in your code - connection = create_database_connection(db_url) - client = APIClient(api_key) - - return process_data(connection, client) - -# Create secret from .env file -@app.local_entrypoint() -def main(): - # Load secrets from local .env file - local_secrets = modal.Secret.from_dotenv(".env") - - # Deploy function with secrets - result = database_operation.remote() - print(result) -``` - -### Proxy - Static Outbound IP Address - -Proxy objects provide Modal containers with static outbound IP addresses for connecting to services that require IP whitelisting. - -```python { .api } -class Proxy: - @classmethod - def from_name(cls, name: str, *, environment_name: Optional[str] = None) -> "Proxy": - """Reference a Proxy by its name (must be provisioned via Dashboard)""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -# Reference proxy created in Modal Dashboard -proxy = modal.Proxy.from_name("production-proxy") - -@app.function(proxy=proxy) -def connect_to_whitelisted_service(): - import requests - - # This request will come from the proxy's static IP - response = requests.get("https://api.partner.com/data") - - # The partner service sees requests from the proxy's static IP - # which can be added to their IP whitelist - return response.json() - -# Multiple functions can share the same proxy -@app.function(proxy=proxy) -def another_whitelisted_operation(): - # Also uses the same static IP - return make_api_call() -``` - -### Tunnel - Network Tunnel for Remote Services - -Network tunnels enable secure connections between Modal functions and remote services, useful for development and accessing services behind firewalls. - -```python { .api } -class Tunnel: - @classmethod - def create( - cls, - *, - host: str = "0.0.0.0", - port: int = 8000, - timeout: Optional[int] = None - ) -> "Tunnel": - """Create a network tunnel""" - -def forward(port: int, *, host: str = "localhost") -> str: - """Forward network traffic through tunnel (utility function)""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -@app.function() -def create_development_tunnel(): - # Create tunnel for development access - tunnel = modal.Tunnel.create(port=8000) - - # Start a simple web server - from http.server import HTTPServer, SimpleHTTPRequestHandler - - server = HTTPServer(("0.0.0.0", 8000), SimpleHTTPRequestHandler) - print(f"Server accessible via tunnel: {tunnel.url}") - - # Server runs and is accessible through tunnel - server.serve_forever() - -@app.function() -def connect_through_tunnel(): - # Forward local service through tunnel - tunnel_url = modal.forward(5432, host="database.internal") - - # Connect to database through tunnel - connection = create_connection(tunnel_url) - return query_database(connection) -``` - -### SchedulerPlacement - Control Function Placement - -Control where functions are scheduled to run, enabling optimization for latency, compliance, or resource availability. - -```python { .api } -class SchedulerPlacement: - @classmethod - def zone(cls, zone: str) -> "SchedulerPlacement": - """Set preferred zone/region for function execution""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -# Place functions in specific zones for latency optimization -us_east_placement = modal.SchedulerPlacement.zone("us-east-1") -eu_placement = modal.SchedulerPlacement.zone("eu-west-1") - -@app.function(scheduler_placement=us_east_placement) -def process_us_data(): - # This function will preferentially run in us-east-1 - # for low latency to US-based services - return fetch_and_process_us_data() - -@app.function(scheduler_placement=eu_placement) -def process_eu_data(): - # This function will preferentially run in eu-west-1 - # for GDPR compliance and low latency to EU services - return fetch_and_process_eu_data() - -@app.function() -def coordinator(): - # Start processing in appropriate regions - us_result = process_us_data.spawn() - eu_result = process_eu_data.spawn() - - # Collect results - return { - "us": us_result.get(), - "eu": eu_result.get() - } -``` - -## Infrastructure Patterns - -### Multi-Environment Secret Management - -```python -import modal - -app = modal.App() - -# Different secrets for different environments -dev_secrets = modal.Secret.from_dict({ - "DATABASE_URL": "postgresql://localhost:5432/dev_db", - "API_ENDPOINT": "https://api-dev.example.com", - "DEBUG": "true" -}) - -prod_secrets = modal.Secret.from_name("production-secrets") - -# Environment-specific function deployment -@app.function(secrets=[dev_secrets]) -def dev_function(): - return process_with_dev_config() - -@app.function(secrets=[prod_secrets]) -def prod_function(): - return process_with_prod_config() -``` - -### Secure Multi-Service Integration - -```python -import modal - -app = modal.App() - -# Multiple secrets for different services -database_secret = modal.Secret.from_name("postgres-credentials") -api_secret = modal.Secret.from_name("external-api-keys") -cloud_secret = modal.Secret.from_name("aws-credentials") - -# Static IP for whitelisted services -proxy = modal.Proxy.from_name("main-proxy") - -@app.function( - secrets=[database_secret, api_secret, cloud_secret], - proxy=proxy -) -def integrated_data_pipeline(): - import os - import boto3 - import psycopg2 - import requests - - # Database connection using secret - db_conn = psycopg2.connect(os.environ["DATABASE_URL"]) - - # AWS client using secret - s3_client = boto3.client( - 's3', - aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"], - aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"] - ) - - # API call using secret and proxy (for static IP) - headers = {"Authorization": f"Bearer {os.environ['API_TOKEN']}"} - response = requests.get("https://api.partner.com/data", headers=headers) - - # Process data across all services - data = response.json() - processed = transform_data(data) - - # Store in database - store_in_database(db_conn, processed) - - # Backup to S3 - backup_to_s3(s3_client, processed) - - return {"status": "success", "records": len(processed)} -``` - -### Geographically Distributed Processing - -```python -import modal - -app = modal.App() - -# Regional placement for compliance and latency -us_placement = modal.SchedulerPlacement.zone("us-east-1") -eu_placement = modal.SchedulerPlacement.zone("eu-west-1") -asia_placement = modal.SchedulerPlacement.zone("ap-southeast-1") - -# Regional secrets for compliance -us_secrets = modal.Secret.from_name("us-compliance-keys") -eu_secrets = modal.Secret.from_name("eu-gdpr-keys") -asia_secrets = modal.Secret.from_name("asia-keys") - -@app.function( - scheduler_placement=us_placement, - secrets=[us_secrets] -) -def process_us_customer_data(customer_ids: list[str]): - # Process US customer data in US region for compliance - return [process_customer(id) for id in customer_ids] - -@app.function( - scheduler_placement=eu_placement, - secrets=[eu_secrets] -) -def process_eu_customer_data(customer_ids: list[str]): - # Process EU customer data in EU region for GDPR compliance - return [process_customer_gdpr(id) for id in customer_ids] - -@app.function() -def global_customer_processing(): - # Route customers to appropriate regional processors - us_customers = get_us_customers() - eu_customers = get_eu_customers() - - # Process in appropriate regions simultaneously - us_task = process_us_customer_data.spawn(us_customers) - eu_task = process_eu_customer_data.spawn(eu_customers) - - # Collect results - us_results = us_task.get() - eu_results = eu_task.get() - - return combine_regional_results(us_results, eu_results) -``` - -### Development to Production Pipeline - -```python -import modal - -# Development setup -dev_app = modal.App("data-pipeline-dev") -dev_secrets = modal.Secret.from_dict({ - "DATABASE_URL": "postgresql://localhost:5432/dev", - "API_KEY": "dev_key_123", - "S3_BUCKET": "dev-data-bucket" -}) - -# Production setup -prod_app = modal.App("data-pipeline-prod") -prod_secrets = modal.Secret.from_name("production-secrets") -prod_proxy = modal.Proxy.from_name("production-proxy") - -# Shared function logic with environment-specific configuration -def create_pipeline_function(app, secrets, proxy=None): - @app.function(secrets=secrets, proxy=proxy) - def data_pipeline(): - import os - - # Environment variables automatically injected based on secrets - db_url = os.environ["DATABASE_URL"] - api_key = os.environ["API_KEY"] - bucket = os.environ["S3_BUCKET"] - - # Same processing logic, different configurations - return process_data(db_url, api_key, bucket) - - return data_pipeline - -# Create environment-specific functions -dev_pipeline = create_pipeline_function(dev_app, [dev_secrets]) -prod_pipeline = create_pipeline_function(prod_app, [prod_secrets], prod_proxy) - -# Deploy based on environment -if os.getenv("ENVIRONMENT") == "production": - prod_pipeline.remote() -else: - dev_pipeline.remote() -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md b/.tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md deleted file mode 100644 index 8317a9bb2..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/runtime-utilities.md +++ /dev/null @@ -1,626 +0,0 @@ -# Runtime Utilities - -Modal provides essential runtime utilities for execution context awareness, debugging, output control, and network tunneling within Modal functions. These utilities enable functions to understand their execution environment and provide enhanced debugging and development capabilities. - -## Capabilities - -### Execution Context Functions - -Functions for understanding the current execution context and accessing runtime information. - -```python { .api } -def current_function_call_id() -> Optional[str]: - """Get the ID of the currently executing function call""" - -def current_input_id() -> Optional[str]: - """Get the ID of the current function input""" - -def is_local() -> bool: - """Check if code is running locally vs in Modal cloud""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("context-aware") - -@app.function() -def context_aware_function(data: str): - """Function that uses runtime context information""" - - # Check execution environment - if modal.is_local(): - print("Running locally - using development configuration") - debug_mode = True - log_level = "DEBUG" - else: - print("Running in Modal cloud - using production configuration") - debug_mode = False - log_level = "INFO" - - # Get runtime identifiers - call_id = modal.current_function_call_id() - input_id = modal.current_input_id() - - print(f"Function call ID: {call_id}") - print(f"Input ID: {input_id}") - - # Use context for logging and tracing - log_entry = { - "timestamp": time.time(), - "function_call_id": call_id, - "input_id": input_id, - "environment": "local" if modal.is_local() else "cloud", - "data": data, - "debug_mode": debug_mode - } - - # Process data with context-aware behavior - if debug_mode: - result = process_with_detailed_logging(data, log_entry) - else: - result = process_efficiently(data, log_entry) - - return { - "result": result, - "call_id": call_id, - "input_id": input_id, - "environment": "local" if modal.is_local() else "cloud" - } - -@app.local_entrypoint() -def main(): - # Test locally - local_result = context_aware_function("local_test_data") - print("Local result:", local_result) - - # Test in cloud - cloud_result = context_aware_function.remote("cloud_test_data") - print("Cloud result:", cloud_result) -``` - -### Interactive Debugging - -Function for enabling interactive debugging sessions within Modal containers. - -```python { .api } -def interact() -> None: - """Start an interactive debugging session (only works in Modal containers)""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("debugging") - -@app.function() -def debug_function(data: dict): - """Function with interactive debugging capabilities""" - - print("Starting data processing...") - - # Process first part - intermediate_result = preprocess_data(data) - - # Interactive debugging point - if should_debug(data): - print("Entering interactive debugging mode...") - print("Available variables: data, intermediate_result") - print("Type 'continue' or Ctrl+D to exit debugging") - - # This opens an interactive Python shell inside the container - modal.interact() - - # Continue processing after debugging - final_result = postprocess_data(intermediate_result) - - return final_result - -def should_debug(data: dict) -> bool: - """Determine if debugging should be enabled""" - # Enable debugging for specific conditions - return ( - data.get("debug_flag", False) or - data.get("complexity_score", 0) > 0.8 or - "error_prone_input" in str(data) - ) - -@app.local_entrypoint() -def main(): - # Normal execution - result1 = debug_function.remote({"value": 42}) - print("Normal result:", result1) - - # Execution with debugging - result2 = debug_function.remote({ - "value": 42, - "debug_flag": True, - "complex_data": [1, 2, 3, 4, 5] - }) - print("Debug result:", result2) -``` - -### Output Control - -Function for controlling output display and progress indicators during Modal function execution. - -```python { .api } -def enable_output(show_progress: bool = True) -> None: - """Enable output streaming for functions (context manager)""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("output-control") - -@app.function() -def processing_function(items: list[str]) -> list[str]: - """Function that processes items with output""" - results = [] - - print("Starting batch processing...") - - for i, item in enumerate(items): - # Print progress - print(f"Processing item {i+1}/{len(items)}: {item}") - - # Simulate processing - result = expensive_operation(item) - results.append(result) - - # Periodic status updates - if (i + 1) % 10 == 0: - print(f"Completed {i+1} items, {len(items) - i - 1} remaining") - - print("Batch processing completed!") - return results - -@app.function() -def map_processing_function(item: str) -> str: - """Function for use with map() that shows progress""" - print(f"Processing: {item}") - result = complex_processing(item) - print(f"Completed: {item} -> {result}") - return result - -@app.local_entrypoint() -def main(): - # Enable output to see function logs and progress - with modal.enable_output(show_progress=True): - - # Single function with output - items = [f"item_{i}" for i in range(50)] - batch_result = processing_function.remote(items) - print(f"Batch processing completed: {len(batch_result)} results") - - # Map with progress tracking - map_items = [f"map_item_{i}" for i in range(20)] - map_results = list(map_processing_function.map(map_items)) - print(f"Map processing completed: {len(map_results)} results") - -# Alternative: Enable output for specific operations -@app.local_entrypoint() -def selective_output(): - """Enable output only for specific operations""" - - # Normal execution (no output) - quiet_result = processing_function.remote([f"quiet_{i}" for i in range(5)]) - - # With output enabled - with modal.enable_output(): - verbose_result = processing_function.remote([f"verbose_{i}" for i in range(5)]) - - print("Selective output demo completed") -``` - -### Network Tunneling - -Function for forwarding network traffic through tunnels, enabling secure connections to remote services. - -```python { .api } -def forward(port: int, *, host: str = "localhost") -> str: - """Forward network traffic through tunnel""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("network-tunneling") - -@app.function() -def tunnel_database_connection(): - """Connect to database through network tunnel""" - - # Forward database port through tunnel - tunnel_url = modal.forward(5432, host="internal-database.company.com") - - print(f"Database tunnel established: {tunnel_url}") - - # Connect to database through tunnel - import psycopg2 - connection = psycopg2.connect( - host=tunnel_url.split("://")[1].split(":")[0], - port=int(tunnel_url.split(":")[-1]), - database="production_db", - user="app_user", - password=os.environ["DB_PASSWORD"] - ) - - # Use the connection - cursor = connection.cursor() - cursor.execute("SELECT COUNT(*) FROM users") - count = cursor.fetchone()[0] - - connection.close() - return {"user_count": count, "tunnel_url": tunnel_url} - -@app.function() -def tunnel_api_access(): - """Access internal API through tunnel""" - - # Forward API port - api_tunnel = modal.forward(8080, host="internal-api.company.com") - - print(f"API tunnel established: {api_tunnel}") - - # Make requests through tunnel - import requests - response = requests.get(f"{api_tunnel}/api/v1/data") - - return { - "api_response": response.json(), - "status_code": response.status_code, - "tunnel_url": api_tunnel - } - -@app.function() -def multi_service_tunneling(): - """Connect to multiple services through tunnels""" - - # Set up multiple tunnels - database_tunnel = modal.forward(5432, host="db.internal") - redis_tunnel = modal.forward(6379, host="redis.internal") - elasticsearch_tunnel = modal.forward(9200, host="es.internal") - - services = { - "database": database_tunnel, - "redis": redis_tunnel, - "elasticsearch": elasticsearch_tunnel - } - - print("All tunnels established:") - for service, url in services.items(): - print(f" {service}: {url}") - - # Use services through tunnels - results = {} - - # Database query - db_result = query_database_through_tunnel(database_tunnel) - results["database"] = db_result - - # Redis operation - redis_result = access_redis_through_tunnel(redis_tunnel) - results["redis"] = redis_result - - # Elasticsearch search - es_result = search_elasticsearch_through_tunnel(elasticsearch_tunnel) - results["elasticsearch"] = es_result - - return { - "results": results, - "tunnels": services - } - -def query_database_through_tunnel(tunnel_url: str) -> dict: - """Helper function to query database through tunnel""" - # Extract host and port from tunnel URL - host = tunnel_url.split("://")[1].split(":")[0] - port = int(tunnel_url.split(":")[-1]) - - # Connect and query - connection = create_db_connection(host, port) - cursor = connection.cursor() - cursor.execute("SELECT version()") - version = cursor.fetchone()[0] - connection.close() - - return {"version": version, "status": "connected"} - -def access_redis_through_tunnel(tunnel_url: str) -> dict: - """Helper function to access Redis through tunnel""" - import redis - - host = tunnel_url.split("://")[1].split(":")[0] - port = int(tunnel_url.split(":")[-1]) - - client = redis.Redis(host=host, port=port) - client.set("tunnel_test", "success") - value = client.get("tunnel_test").decode() - - return {"test_value": value, "status": "connected"} - -@app.local_entrypoint() -def main(): - # Test database tunnel - db_result = tunnel_database_connection.remote() - print("Database tunnel result:", db_result) - - # Test API tunnel - api_result = tunnel_api_access.remote() - print("API tunnel result:", api_result) - - # Test multiple tunnels - multi_result = multi_service_tunneling.remote() - print("Multi-service tunnel result:", multi_result) -``` - -## Advanced Runtime Patterns - -### Context-Aware Error Handling - -```python -import modal - -app = modal.App("context-error-handling") - -@app.function() -def robust_function(data: dict): - """Function with context-aware error handling""" - - call_id = modal.current_function_call_id() - input_id = modal.current_input_id() - is_local_env = modal.is_local() - - error_context = { - "call_id": call_id, - "input_id": input_id, - "environment": "local" if is_local_env else "cloud", - "timestamp": time.time() - } - - try: - result = risky_operation(data) - return {"success": True, "result": result, "context": error_context} - - except Exception as e: - # Enhanced error information with runtime context - error_info = { - "error": str(e), - "error_type": type(e).__name__, - "context": error_context, - "data_summary": { - "size": len(str(data)), - "keys": list(data.keys()) if isinstance(data, dict) else None - } - } - - # Different handling based on environment - if is_local_env: - print("Local error - entering debug mode") - print(f"Error context: {error_info}") - modal.interact() # Interactive debugging in local mode - else: - print("Cloud error - logging and continuing") - log_error_to_monitoring_system(error_info) - - return {"success": False, "error": error_info} -``` - -### Distributed Tracing with Context - -```python -import modal - -app = modal.App("distributed-tracing") - -# Shared tracing storage -trace_store = modal.Dict.persist("trace-data") - -@app.function() -def traced_function_a(data: str): - """First function in trace chain""" - - call_id = modal.current_function_call_id() - trace_id = f"trace_{int(time.time())}" - - # Start trace - trace_entry = { - "trace_id": trace_id, - "function": "traced_function_a", - "call_id": call_id, - "start_time": time.time(), - "input_data": data - } - - print(f"Starting trace {trace_id} in function A") - - # Process data - result_a = process_step_a(data) - - # Call next function with trace context - result_b = traced_function_b.remote(result_a, trace_id) - - # Complete trace entry - trace_entry.update({ - "end_time": time.time(), - "result": "completed", - "next_call": "traced_function_b" - }) - - # Store trace data - trace_store[f"{trace_id}_function_a"] = trace_entry - - return {"result": result_b, "trace_id": trace_id} - -@app.function() -def traced_function_b(data: str, trace_id: str): - """Second function in trace chain""" - - call_id = modal.current_function_call_id() - - trace_entry = { - "trace_id": trace_id, - "function": "traced_function_b", - "call_id": call_id, - "start_time": time.time(), - "input_data": data - } - - print(f"Continuing trace {trace_id} in function B") - - # Process data - result = process_step_b(data) - - # Complete trace - trace_entry.update({ - "end_time": time.time(), - "result": result - }) - - trace_store[f"{trace_id}_function_b"] = trace_entry - - return result - -@app.function() -def get_trace_summary(trace_id: str): - """Get complete trace summary""" - - # Collect all trace entries - trace_entries = {} - for key in trace_store.keys(): - if key.startswith(trace_id): - trace_entries[key] = trace_store.get(key) - - # Calculate trace metrics - start_times = [entry["start_time"] for entry in trace_entries.values()] - end_times = [entry["end_time"] for entry in trace_entries.values()] - - total_duration = max(end_times) - min(start_times) - - return { - "trace_id": trace_id, - "total_duration": total_duration, - "function_count": len(trace_entries), - "entries": trace_entries - } - -@app.local_entrypoint() -def main(): - with modal.enable_output(): - # Execute traced function chain - result = traced_function_a.remote("test_data") - - # Get trace summary - trace_summary = get_trace_summary.remote(result["trace_id"]) - print("Trace summary:", trace_summary) -``` - -### Development vs Production Runtime - -```python -import modal - -app = modal.App("environment-aware") - -@app.function() -def environment_aware_processing(data: dict): - """Function that adapts behavior based on runtime environment""" - - is_local_env = modal.is_local() - call_id = modal.current_function_call_id() - - # Environment-specific configuration - if is_local_env: - config = { - "timeout": 30, - "retries": 1, - "logging_level": "DEBUG", - "use_cache": False, - "external_apis": "staging" - } - print("Using development configuration") - else: - config = { - "timeout": 300, - "retries": 3, - "logging_level": "INFO", - "use_cache": True, - "external_apis": "production" - } - print("Using production configuration") - - print(f"Processing with config: {config}") - print(f"Call ID: {call_id}") - - # Adaptive processing based on environment - try: - if config["use_cache"]: - # Check cache first in production - cached_result = check_cache(data) - if cached_result: - return {"result": cached_result, "source": "cache", "config": config} - - # Process with environment-specific settings - result = process_with_config(data, config) - - if config["use_cache"]: - # Cache result in production - store_in_cache(data, result) - - return {"result": result, "source": "computed", "config": config} - - except Exception as e: - error_info = { - "error": str(e), - "environment": "local" if is_local_env else "production", - "call_id": call_id, - "config": config - } - - if is_local_env: - # More verbose error handling in development - print(f"Development error: {error_info}") - modal.interact() # Debug interactively - - return {"error": error_info, "success": False} - -def process_with_config(data: dict, config: dict): - """Process data with environment-specific configuration""" - - # Simulate processing with different timeouts - import time - processing_time = min(config["timeout"] / 10, 5) # Simulate work - time.sleep(processing_time) - - # Different API endpoints based on environment - api_base = f"https://api-{config['external_apis']}.example.com" - - return { - "processed_data": f"processed_{data}", - "api_used": api_base, - "processing_time": processing_time - } - -@app.local_entrypoint() -def main(): - test_data = {"input": "test_value", "timestamp": time.time()} - - with modal.enable_output(): - # This will use development config when run locally - local_result = environment_aware_processing(test_data) - print("Local result:", local_result) - - # This will use production config when run in Modal cloud - cloud_result = environment_aware_processing.remote(test_data) - print("Cloud result:", cloud_result) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md b/.tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md deleted file mode 100644 index b40add20b..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/scheduling-reliability.md +++ /dev/null @@ -1,456 +0,0 @@ -# Scheduling & Reliability - -Modal provides comprehensive scheduling and reliability features for automated task execution and resilient function behavior. These capabilities enable building robust serverless applications with automatic retry policies and flexible scheduling patterns. - -## Capabilities - -### Cron Scheduling - -Schedule functions to run at specific times using Unix cron syntax, supporting timezone-aware scheduling for automated tasks. - -```python { .api } -class Cron: - def __init__(self, cron_string: str, timezone: str = "UTC") -> None: - """Create a cron schedule from cron expression string""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("scheduled-tasks") - -# Run every minute -@app.function(schedule=modal.Cron("* * * * *")) -def check_system_health(): - """Monitor system health every minute""" - health_status = check_all_services() - if not health_status["healthy"]: - send_alert(health_status["issues"]) - return health_status - -# Run daily at 4:05 AM UTC -@app.function(schedule=modal.Cron("5 4 * * *")) -def daily_backup(): - """Perform daily database backup""" - print("Starting daily backup...") - backup_result = create_database_backup() - upload_to_cloud_storage(backup_result) - cleanup_old_backups() - print("Daily backup completed") - -# Run every Thursday at 9 AM UTC -@app.function(schedule=modal.Cron("0 9 * * 4")) -def weekly_report(): - """Generate weekly analytics report""" - report_data = generate_weekly_analytics() - send_report_email(report_data) - store_report(report_data) - -# Run daily at 6 AM New York time (timezone-aware) -@app.function(schedule=modal.Cron("0 6 * * *", timezone="America/New_York")) -def business_hours_task(): - """Task that runs at business hours in New York""" - # Automatically adjusts for daylight saving time - process_business_data() - -# Complex cron expressions -@app.function(schedule=modal.Cron("15 2 * * 1-5")) # Weekdays at 2:15 AM -def weekday_maintenance(): - """Maintenance tasks during weekdays""" - perform_system_maintenance() - -@app.function(schedule=modal.Cron("0 12 1 * *")) # First day of month at noon -def monthly_billing(): - """Process monthly billing on first day of month""" - process_monthly_invoices() - send_billing_notifications() -``` - -### Period Scheduling - -Schedule functions to run at regular intervals using natural time periods, supporting precise timing for recurring tasks. - -```python { .api } -class Period: - def __init__( - self, - *, - years: int = 0, - months: int = 0, - weeks: int = 0, - days: int = 0, - hours: int = 0, - minutes: int = 0, - seconds: float = 0 - ) -> None: - """Create a periodic schedule with specified intervals""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("periodic-tasks") - -# Run every day -@app.function(schedule=modal.Period(days=1)) -def daily_data_sync(): - """Synchronize data daily""" - sync_external_apis() - update_local_cache() - print("Daily sync completed") - -# Run every 4 hours -@app.function(schedule=modal.Period(hours=4)) -def frequent_health_check(): - """Check system health every 4 hours""" - status = perform_detailed_health_check() - log_health_metrics(status) - -# Run every 15 minutes -@app.function(schedule=modal.Period(minutes=15)) -def cache_refresh(): - """Refresh application cache every 15 minutes""" - refresh_application_cache() - update_cache_metrics() - -# Run every 30 seconds -@app.function(schedule=modal.Period(seconds=30)) -def high_frequency_monitor(): - """High-frequency monitoring task""" - collect_realtime_metrics() - check_alert_conditions() - -# Complex periods -@app.function(schedule=modal.Period(weeks=2, days=3)) # Every 17 days -def bi_weekly_plus_task(): - """Task that runs every 2 weeks + 3 days""" - perform_special_analysis() - -@app.function(schedule=modal.Period(months=1)) # Monthly (same day each month) -def monthly_report(): - """Generate monthly report on same day each month""" - # Handles varying month lengths correctly - generate_monthly_analytics() - archive_old_data() - -# High precision timing -@app.function(schedule=modal.Period(seconds=3.14159)) # Every π seconds -def precise_timing_task(): - """Task with precise timing requirements""" - collect_high_precision_measurements() -``` - -### Retry Policies - -Configure automatic retry behavior for functions to handle transient failures and improve reliability. - -```python { .api } -class Retries: - def __init__( - self, - *, - max_retries: int, - backoff_coefficient: float = 2.0, - initial_delay: float = 1.0, - max_delay: float = 60.0 - ) -> None: - """Configure retry policy with exponential or fixed backoff""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("reliable-functions") - -# Simple retry configuration -@app.function(retries=4) # Retry up to 4 times with default settings -def simple_retry_task(): - """Task with basic retry policy""" - result = call_external_api() - return result - -# Fixed-interval retries -@app.function( - retries=modal.Retries( - max_retries=3, - backoff_coefficient=1.0, # Fixed delay - initial_delay=5.0, # 5 seconds between retries - ) -) -def fixed_retry_task(): - """Task with fixed 5-second delay between retries""" - try: - return process_critical_data() - except Exception as e: - print(f"Attempt failed: {e}") - raise # Will trigger retry - -# Exponential backoff -@app.function( - retries=modal.Retries( - max_retries=5, - backoff_coefficient=2.0, # Double delay each retry - initial_delay=1.0, # Start with 1 second - max_delay=30.0 # Cap at 30 seconds - ) -) -def exponential_backoff_task(): - """Task with exponential backoff: 1s, 2s, 4s, 8s, 16s delays""" - return call_rate_limited_service() - -# Custom backoff configuration -@app.function( - retries=modal.Retries( - max_retries=6, - backoff_coefficient=1.5, # 50% increase each retry - initial_delay=2.0, # Start with 2 seconds - max_delay=60.0 # Cap at 1 minute - ) -) -def custom_backoff_task(): - """Custom retry pattern: 2s, 3s, 4.5s, 6.75s, 10.125s, 15.1875s""" - return perform_database_operation() - -# Retry with error handling -@app.function( - retries=modal.Retries( - max_retries=3, - initial_delay=1.0, - backoff_coefficient=2.0 - ) -) -def resilient_task(): - """Task with retry and comprehensive error handling""" - attempt = 0 - try: - # Simulate getting current attempt (in real usage, this would be tracked differently) - result = unreliable_external_service() - print(f"Success on attempt {attempt + 1}") - return result - except TemporaryError as e: - print(f"Temporary error on attempt {attempt + 1}: {e}") - raise # Retry - except PermanentError as e: - print(f"Permanent error, not retrying: {e}") - return {"error": "permanent_failure", "message": str(e)} -``` - -## Advanced Scheduling Patterns - -### Multi-Timezone Scheduling - -```python -import modal - -app = modal.App("global-scheduling") - -# Different tasks in different timezones -@app.function(schedule=modal.Cron("0 9 * * *", timezone="America/New_York")) -def us_business_hours(): - """Run at 9 AM Eastern Time""" - process_us_customer_data() - -@app.function(schedule=modal.Cron("0 9 * * *", timezone="Europe/London")) -def uk_business_hours(): - """Run at 9 AM London Time""" - process_uk_customer_data() - -@app.function(schedule=modal.Cron("0 9 * * *", timezone="Asia/Tokyo")) -def japan_business_hours(): - """Run at 9 AM Japan Time""" - process_japan_customer_data() - -@app.function(schedule=modal.Cron("0 0 * * *", timezone="UTC")) -def global_midnight_task(): - """Run at midnight UTC (global coordination)""" - consolidate_global_reports() -``` - -### Cascading Scheduled Tasks - -```python -import modal - -app = modal.App("cascading-tasks") - -# Shared queue for task coordination -task_queue = modal.Queue.persist("cascade-queue") - -@app.function(schedule=modal.Cron("0 2 * * *")) # 2 AM daily -def stage_1_data_extraction(): - """First stage: Extract data""" - print("Stage 1: Starting data extraction...") - raw_data = extract_raw_data() - - # Queue next stage - task_queue.put({ - "stage": "transform", - "data_id": raw_data["id"], - "timestamp": time.time() - }) - - print("Stage 1: Data extraction completed") - -@app.function(schedule=modal.Period(minutes=5)) # Check every 5 minutes -def stage_2_data_transformation(): - """Second stage: Transform data when available""" - try: - task = task_queue.get(timeout=1) # Non-blocking check - if task and task["stage"] == "transform": - print(f"Stage 2: Transforming data {task['data_id']}") - transformed_data = transform_data(task["data_id"]) - - # Queue final stage - task_queue.put({ - "stage": "load", - "data_id": transformed_data["id"], - "timestamp": time.time() - }) - - print("Stage 2: Data transformation completed") - except: - # No tasks available, continue - pass - -@app.function(schedule=modal.Period(minutes=10)) # Check every 10 minutes -def stage_3_data_loading(): - """Final stage: Load data when ready""" - try: - task = task_queue.get(timeout=1) - if task and task["stage"] == "load": - print(f"Stage 3: Loading data {task['data_id']}") - load_data_to_warehouse(task["data_id"]) - send_completion_notification(task["data_id"]) - print("Stage 3: Data loading completed") - except: - pass -``` - -### Conditional Scheduling with Circuit Breaker - -```python -import modal - -app = modal.App("reliable-scheduling") - -# Shared state for circuit breaker -circuit_state = modal.Dict.persist("circuit-breaker") - -@app.function( - schedule=modal.Period(minutes=10), - retries=modal.Retries(max_retries=2, initial_delay=30.0) -) -def monitored_task(): - """Task with circuit breaker pattern""" - # Check circuit breaker state - is_open = circuit_state.get("circuit_open", False) - failure_count = circuit_state.get("failure_count", 0) - last_failure = circuit_state.get("last_failure_time", 0) - - # Circuit breaker logic - if is_open: - # Check if enough time has passed to try again - if time.time() - last_failure > 300: # 5 minutes - print("Circuit breaker: Attempting to close circuit") - circuit_state["circuit_open"] = False - else: - print("Circuit breaker: Circuit is open, skipping execution") - return {"status": "circuit_open"} - - try: - # Attempt the operation - result = potentially_failing_operation() - - # Success - reset failure count - circuit_state["failure_count"] = 0 - circuit_state["circuit_open"] = False - - print("Task completed successfully") - return {"status": "success", "result": result} - - except Exception as e: - # Handle failure - new_failure_count = failure_count + 1 - circuit_state["failure_count"] = new_failure_count - circuit_state["last_failure_time"] = time.time() - - # Open circuit if too many failures - if new_failure_count >= 3: - circuit_state["circuit_open"] = True - print("Circuit breaker: Opening circuit due to repeated failures") - - print(f"Task failed (attempt {new_failure_count}): {e}") - raise # Will trigger retry policy -``` - -### Dynamic Scheduling Based on Load - -```python -import modal - -app = modal.App("adaptive-scheduling") - -# Shared metrics storage -metrics = modal.Dict.persist("system-metrics") - -@app.function(schedule=modal.Period(seconds=30)) -def collect_system_metrics(): - """Collect system metrics every 30 seconds""" - current_load = get_system_load() - queue_depth = get_queue_depth() - error_rate = get_error_rate() - - metrics.update({ - "load": current_load, - "queue_depth": queue_depth, - "error_rate": error_rate, - "timestamp": time.time() - }) - -@app.function(schedule=modal.Period(minutes=1)) -def adaptive_processor(): - """Process tasks with adaptive frequency based on load""" - current_metrics = metrics.get("load", 0) - queue_depth = metrics.get("queue_depth", 0) - - # Adapt processing based on system state - if current_metrics < 0.5 and queue_depth > 100: - # Low load, high queue - process more aggressively - batch_size = 50 - process_batch_tasks(batch_size) - print(f"High throughput mode: processed {batch_size} tasks") - - elif current_metrics > 0.8: - # High load - reduce processing - batch_size = 10 - process_batch_tasks(batch_size) - print(f"Conservative mode: processed {batch_size} tasks") - - else: - # Normal processing - batch_size = 25 - process_batch_tasks(batch_size) - print(f"Normal mode: processed {batch_size} tasks") - -@app.function( - schedule=modal.Period(minutes=5), - retries=modal.Retries(max_retries=3, initial_delay=60.0) -) -def health_based_processing(): - """Processing task that adapts based on system health""" - error_rate = metrics.get("error_rate", 0) - - if error_rate > 0.1: # More than 10% error rate - print("High error rate detected, running diagnostic tasks") - run_system_diagnostics() - return {"status": "diagnostics"} - else: - print("System healthy, running normal processing") - return process_normal_workload() -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/storage-data.md b/.tessl/tiles/tessl/pypi-modal/docs/storage-data.md deleted file mode 100644 index 2d147b6c6..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/storage-data.md +++ /dev/null @@ -1,528 +0,0 @@ -# Storage & Data - -Modal provides comprehensive storage solutions for persisting data across function calls, including volumes, network file systems, key-value stores, queues, and cloud bucket mounts. These storage primitives enable building stateful applications while maintaining the benefits of serverless architecture. - -## Capabilities - -### Volume - Persistent Networked File System - -Persistent networked file system storage that provides POSIX-like file operations and can be mounted to multiple functions simultaneously. - -```python { .api } -class Volume: - @classmethod - def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Volume": - """Load a Volume by its unique name""" - - @classmethod - def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "Volume": - """Create a persistent Volume with given name""" - - @classmethod - def ephemeral(cls, **kwargs) -> "Volume": - """Create an ephemeral Volume that's deleted when not in use""" - - def listdir(self, path: str) -> list[FileEntry]: - """List files and directories at path""" - - def iterdir(self, path: str) -> AsyncIterator[FileEntry]: - """Async iterator over files and directories at path""" - - def put_file( - self, - local_file: Union[str, Path, BinaryIO], - remote_path: str, - *, - progress: Optional[bool] = None - ) -> None: - """Upload a local file to the volume""" - - def put_directory( - self, - local_path: Union[str, Path], - remote_path: str, - *, - pattern: Optional[str] = None, - progress: Optional[bool] = None - ) -> None: - """Upload a local directory to the volume""" - - def get_file(self, remote_path: str, local_file: Union[str, Path, BinaryIO]) -> None: - """Download a file from the volume to local storage""" - - def remove_file(self, path: str, *, recursive: bool = False) -> None: - """Remove a file or directory from the volume""" - - def exists(self, path: str) -> bool: - """Check if a path exists in the volume""" - - def reload(self) -> None: - """Reload the volume to get latest state""" - -class FileEntry: - """Represents a file or directory entry in a volume""" - path: str - type: FileEntryType # FILE, DIRECTORY, SYMLINK, etc. - mtime: int # Modified time as Unix timestamp - size: int # Size in bytes - -class FileEntryType: - """Type of file entry""" - FILE: int - DIRECTORY: int - SYMLINK: int - FIFO: int - SOCKET: int -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -# Create a persistent volume -volume = modal.Volume.persist("my-data-volume") - -@app.function(volumes={"/data": volume}) -def process_data(): - # Files are accessible at /data mount point - with open("/data/input.txt", "r") as f: - content = f.read() - - # Process data and save results - with open("/data/output.txt", "w") as f: - f.write(f"Processed: {content}") - -# Upload files to volume from local machine -@app.local_entrypoint() -def upload_data(): - volume.put_file("local_data.txt", "/input.txt") - volume.put_directory("./datasets", "/datasets") - - # List volume contents - for entry in volume.listdir("/"): - print(f"{entry.path}: {entry.type.name}, {entry.size} bytes") -``` - -### NetworkFileSystem - Shared Networked Storage - -Shared networked file system that allows multiple functions to read and write files concurrently with better performance for frequent access patterns. - -```python { .api } -class NetworkFileSystem: - @classmethod - def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "NetworkFileSystem": - """Load a NetworkFileSystem by its unique name""" - - @classmethod - def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "NetworkFileSystem": - """Create a persistent NetworkFileSystem with given name""" - - @classmethod - def ephemeral(cls, **kwargs) -> "NetworkFileSystem": - """Create an ephemeral NetworkFileSystem""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() -nfs = modal.NetworkFileSystem.persist("shared-cache") - -@app.function(network_file_systems={"/cache": nfs}) -def worker_function(task_id: str): - cache_file = f"/cache/task_{task_id}.json" - - # Check if cached result exists - if os.path.exists(cache_file): - with open(cache_file, "r") as f: - return json.load(f) - - # Process and cache result - result = expensive_computation(task_id) - with open(cache_file, "w") as f: - json.dump(result, f) - - return result -``` - -### Dict - Persistent Key-Value Store - -Distributed key-value store for sharing data between functions with automatic serialization and deserialization. - -```python { .api } -class Dict: - @classmethod - def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Dict": - """Load a Dict by its unique name""" - - @classmethod - def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "Dict": - """Create a persistent Dict with given name""" - - @classmethod - def ephemeral(cls, **kwargs) -> "Dict": - """Create an ephemeral Dict""" - - def get(self, key: str, default: Any = None) -> Any: - """Get value by key, returns default if key doesn't exist""" - - def __getitem__(self, key: str) -> Any: - """Get value by key using dict[key] syntax""" - - def put(self, key: str, value: Any) -> None: - """Put key-value pair""" - - def __setitem__(self, key: str, value: Any) -> None: - """Set value using dict[key] = value syntax""" - - def pop(self, key: str, default: Any = None) -> Any: - """Remove and return value for key""" - - def __delitem__(self, key: str) -> None: - """Delete key using del dict[key] syntax""" - - def update(self, mapping: Mapping[str, Any]) -> None: - """Update multiple key-value pairs""" - - def clear(self) -> None: - """Remove all items from the dict""" - - def len(self) -> int: - """Get number of items in the dict""" - - def __len__(self) -> int: - """Get number of items using len(dict) syntax""" - - def contains(self, key: str) -> bool: - """Check if key exists in dict""" - - def __contains__(self, key: str) -> bool: - """Check if key exists using 'key in dict' syntax""" - - def keys(self) -> list[str]: - """Get all keys as a list""" - - def values(self) -> list[Any]: - """Get all values as a list""" - - def items(self) -> list[tuple[str, Any]]: - """Get all key-value pairs as list of tuples""" - - def iterate_keys(self) -> AsyncIterator[str]: - """Async iterator over all keys""" - - def iterate_values(self) -> AsyncIterator[Any]: - """Async iterator over all values""" - - def iterate_items(self) -> AsyncIterator[tuple[str, Any]]: - """Async iterator over all key-value pairs""" - -class DictInfo: - """Information about a Dict object""" - name: Optional[str] - created_at: datetime - created_by: Optional[str] -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() -shared_dict = modal.Dict.persist("config-store") - -@app.function() -def setup_config(): - # Store configuration data - shared_dict["database_url"] = "postgresql://..." - shared_dict["api_keys"] = {"service_a": "key1", "service_b": "key2"} - shared_dict["feature_flags"] = {"new_feature": True, "beta_mode": False} - -@app.function() -def worker_task(): - # Access shared configuration - db_url = shared_dict["database_url"] - api_keys = shared_dict.get("api_keys", {}) - - # Check feature flag - if shared_dict.get("feature_flags", {}).get("new_feature", False): - return use_new_algorithm() - else: - return use_legacy_algorithm() - -@app.function() -def analytics_function(): - # Update metrics - current_count = shared_dict.get("request_count", 0) - shared_dict["request_count"] = current_count + 1 - - # Store processing results - results = shared_dict.get("daily_results", []) - results.append({"timestamp": time.time(), "processed": 100}) - shared_dict["daily_results"] = results -``` - -### Queue - Distributed Task Queue - -Distributed queue for asynchronous task processing with automatic serialization and FIFO ordering. - -```python { .api } -class Queue: - @classmethod - def from_name(cls, label: str, *, environment_name: Optional[str] = None) -> "Queue": - """Load a Queue by its unique name""" - - @classmethod - def persist(cls, label: str, *, environment_name: Optional[str] = None) -> "Queue": - """Create a persistent Queue with given name""" - - @classmethod - def ephemeral(cls, **kwargs) -> "Queue": - """Create an ephemeral Queue""" - - def put(self, item: Any, *, block: bool = True) -> None: - """Put an item into the queue""" - - def put_many(self, items: list[Any], *, block: bool = True) -> None: - """Put multiple items into the queue""" - - def get(self, *, block: bool = True, timeout: Optional[float] = None) -> Any: - """Get an item from the queue""" - - def get_many(self, n: int, *, block: bool = True, timeout: Optional[float] = None) -> list[Any]: - """Get multiple items from the queue""" - - def iterate(self, *, timeout: Optional[float] = None) -> AsyncIterator[Any]: - """Async iterator over queue items""" - - def len(self) -> int: - """Get approximate number of items in queue""" - - def __len__(self) -> int: - """Get approximate number of items using len(queue) syntax""" - -class QueueInfo: - """Information about a Queue object""" - name: Optional[str] - created_at: datetime - created_by: Optional[str] -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() -task_queue = modal.Queue.persist("work-queue") - -# Producer function -@app.function() -def generate_tasks(): - tasks = [{"id": i, "data": f"task_{i}"} for i in range(100)] - task_queue.put_many(tasks) - print(f"Added {len(tasks)} tasks to queue") - -# Consumer function -@app.function() -def process_tasks(): - while True: - try: - # Get task with timeout - task = task_queue.get(timeout=30) - - # Process the task - result = expensive_operation(task["data"]) - print(f"Processed task {task['id']}: {result}") - - except queue.Empty: - print("No more tasks, worker stopping") - break - -# Batch consumer -@app.function() -def batch_processor(): - # Process tasks in batches for efficiency - for batch in task_queue.iterate(): - tasks = task_queue.get_many(10) # Get up to 10 tasks - results = [process_single_task(task) for task in tasks] - print(f"Processed batch of {len(results)} tasks") -``` - -### CloudBucketMount - Cloud Storage Integration - -Mount cloud storage buckets (S3, GCS, Azure) as file systems within Modal functions. - -```python { .api } -class CloudBucketMount: - @classmethod - def from_s3_bucket( - cls, - bucket_name: str, - *, - key_prefix: str = "", - secret: Optional["Secret"] = None, - read_only: bool = True - ) -> "CloudBucketMount": - """Mount an S3 bucket""" - - @classmethod - def from_gcs_bucket( - cls, - bucket_name: str, - *, - key_prefix: str = "", - secret: Optional["Secret"] = None, - read_only: bool = True - ) -> "CloudBucketMount": - """Mount a Google Cloud Storage bucket""" - - @classmethod - def from_azure_blob_storage( - cls, - account_name: str, - container_name: str, - *, - key_prefix: str = "", - secret: Optional["Secret"] = None, - read_only: bool = True - ) -> "CloudBucketMount": - """Mount an Azure Blob Storage container""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App() - -# Mount S3 bucket -s3_mount = modal.CloudBucketMount.from_s3_bucket( - "my-data-bucket", - secret=modal.Secret.from_name("aws-credentials"), - read_only=False -) - -@app.function(cloud_bucket_mounts={"/s3-data": s3_mount}) -def process_s3_data(): - # Access S3 files as local files - with open("/s3-data/input/data.csv", "r") as f: - data = f.read() - - # Process data - result = analyze_data(data) - - # Write results back to S3 - with open("/s3-data/output/results.json", "w") as f: - json.dump(result, f) - -# Mount GCS bucket -gcs_mount = modal.CloudBucketMount.from_gcs_bucket( - "my-gcs-bucket", - secret=modal.Secret.from_name("gcp-credentials") -) - -@app.function(cloud_bucket_mounts={"/gcs-data": gcs_mount}) -def backup_to_gcs(): - # Read local data - local_files = os.listdir("/tmp/data") - - # Copy to GCS through mount - for filename in local_files: - shutil.copy(f"/tmp/data/{filename}", f"/gcs-data/backup/{filename}") -``` - -## Storage Patterns - -### Data Pipeline with Multiple Storage Types - -```python -import modal - -app = modal.App() - -# Different storage for different use cases -raw_data_volume = modal.Volume.persist("raw-data") # Large file storage -processed_cache = modal.NetworkFileSystem.persist("cache") # Fast shared access -config_dict = modal.Dict.persist("pipeline-config") # Configuration -task_queue = modal.Queue.persist("processing-queue") # Task coordination - -@app.function( - volumes={"/raw": raw_data_volume}, - network_file_systems={"/cache": processed_cache} -) -def data_pipeline(): - # Get configuration - batch_size = config_dict.get("batch_size", 100) - - # Process raw data files - for filename in os.listdir("/raw/input"): - # Check cache first - cache_key = f"/cache/processed_{filename}" - if os.path.exists(cache_key): - continue # Already processed - - # Process file - with open(f"/raw/input/{filename}", "r") as f: - data = f.read() - - result = process_data_file(data) - - # Cache result - with open(cache_key, "w") as f: - json.dump(result, f) - - # Queue downstream tasks - task_queue.put({"type": "notify", "file": filename, "result": result}) -``` - -### Shared State Between Functions - -```python -import modal - -app = modal.App() -shared_state = modal.Dict.persist("worker-state") - -@app.function() -def coordinator(): - # Initialize shared state - shared_state["active_workers"] = 0 - shared_state["total_processed"] = 0 - shared_state["status"] = "starting" - - # Start workers - for i in range(5): - worker.spawn(f"worker-{i}") - -@app.function() -def worker(worker_id: str): - # Register worker - current_workers = shared_state.get("active_workers", 0) - shared_state["active_workers"] = current_workers + 1 - - try: - # Do work - for task in get_tasks(): - result = process_task(task) - - # Update shared counters - total = shared_state.get("total_processed", 0) - shared_state["total_processed"] = total + 1 - - finally: - # Unregister worker - current_workers = shared_state.get("active_workers", 0) - shared_state["active_workers"] = max(0, current_workers - 1) - - # Check if all workers done - if shared_state["active_workers"] == 0: - shared_state["status"] = "completed" -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/utility-classes.md b/.tessl/tiles/tessl/pypi-modal/docs/utility-classes.md deleted file mode 100644 index 221119b66..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/utility-classes.md +++ /dev/null @@ -1,791 +0,0 @@ -# Utility Classes - -Modal provides essential utility classes for error handling and file pattern matching. These utilities support robust error management and flexible file filtering capabilities across Modal applications. - -## Capabilities - -### Package Version - -Package version string for the Modal client library. - -```python { .api } -__version__: str # Current package version (e.g., "1.1.4") -``` - -### Error - Base Exception Class - -Base exception class for all Modal-specific errors, providing a hierarchy of specialized error types for different failure scenarios. - -```python { .api } -class Error(Exception): - """Base class for all Modal errors""" - -# Specialized error types -class AlreadyExistsError(Error): - """Raised when a resource creation conflicts with an existing resource""" - -class RemoteError(Error): - """Raised when an error occurs on the Modal server""" - -class TimeoutError(Error): - """Base class for Modal timeouts""" - -class FunctionTimeoutError(TimeoutError): - """Raised when a Function exceeds its execution duration limit""" - -class SandboxTimeoutError(TimeoutError): - """Raised when a Sandbox exceeds its execution duration limit""" - -class VolumeUploadTimeoutError(TimeoutError): - """Raised when a Volume upload times out""" - -class MountUploadTimeoutError(TimeoutError): - """Raised when a Mount upload times out""" - -class InteractiveTimeoutError(TimeoutError): - """Raised when interactive frontends time out while connecting""" - -class OutputExpiredError(TimeoutError): - """Raised when the Output exceeds expiration""" - -class AuthError(Error): - """Raised when a client has missing or invalid authentication""" - -class ConnectionError(Error): - """Raised when an issue occurs while connecting to Modal servers""" - -class InvalidError(Error): - """Raised when user does something invalid""" - -class VersionError(Error): - """Raised when the current client version is unsupported""" - -class NotFoundError(Error): - """Raised when a requested resource was not found""" - -class ExecutionError(Error): - """Raised when something unexpected happened during runtime""" - -class DeserializationError(Error): - """Raised when an error is encountered during deserialization""" - -class SerializationError(Error): - """Raised when an error is encountered during serialization""" -``` - -#### Usage Examples - -```python -import modal - -app = modal.App("error-handling") - -@app.function() -def error_prone_function(data: dict): - """Function demonstrating Modal error handling""" - - try: - # Operation that might fail with different error types - result = risky_operation(data) - return {"success": True, "result": result} - - except modal.FunctionTimeoutError as e: - print(f"Function timed out: {e}") - return {"error": "timeout", "message": str(e)} - - except modal.DeserializationError as e: - print(f"Data deserialization failed: {e}") - return {"error": "invalid_data", "message": str(e)} - - except modal.RemoteError as e: - print(f"Server error occurred: {e}") - return {"error": "server_error", "message": str(e)} - - except modal.Error as e: - # Catch any Modal-specific error - print(f"Modal error: {e}") - return {"error": "modal_error", "type": type(e).__name__, "message": str(e)} - - except Exception as e: - # Non-Modal errors - print(f"Unexpected error: {e}") - return {"error": "unexpected", "message": str(e)} - -@app.function() -def resource_creation_function(resource_name: str): - """Function that handles resource creation errors""" - - try: - # Attempt to create a new resource - volume = modal.Volume.persist(resource_name) - return {"success": True, "resource": resource_name} - - except modal.AlreadyExistsError: - # Resource already exists - use existing one - print(f"Volume {resource_name} already exists, using existing volume") - volume = modal.Volume.from_name(resource_name) - return {"success": True, "resource": resource_name, "status": "existing"} - - except modal.AuthError as e: - print(f"Authentication failed: {e}") - return {"error": "auth_failed", "message": str(e)} - - except modal.InvalidError as e: - print(f"Invalid resource name: {e}") - return {"error": "invalid_name", "message": str(e)} - -@app.function() -def upload_with_timeout_handling(local_path: str, remote_path: str): - """Function that handles upload timeout errors""" - - volume = modal.Volume.from_name("upload-volume") - - try: - volume.put_file(local_path, remote_path) - return {"success": True, "uploaded": remote_path} - - except modal.VolumeUploadTimeoutError as e: - print(f"Volume upload timed out: {e}") - # Implement retry logic or alternative approach - return {"error": "upload_timeout", "retry_recommended": True} - - except modal.NotFoundError as e: - print(f"File not found: {e}") - return {"error": "file_not_found", "path": local_path} - -# Custom error handling with context -def handle_modal_errors(func): - """Decorator for comprehensive Modal error handling""" - def wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - except modal.TimeoutError as e: - return {"error": "timeout", "type": type(e).__name__, "message": str(e)} - except modal.AuthError as e: - return {"error": "authentication", "message": str(e)} - except modal.ConnectionError as e: - return {"error": "connection", "message": str(e)} - except modal.Error as e: - return {"error": "modal", "type": type(e).__name__, "message": str(e)} - except Exception as e: - return {"error": "unknown", "message": str(e)} - return wrapper - -@app.function() -@handle_modal_errors -def decorated_function(data: str): - """Function with decorator-based error handling""" - return process_data_safely(data) - -@app.local_entrypoint() -def main(): - # Test error handling - result1 = error_prone_function.remote({"test": "data"}) - print("Error handling result:", result1) - - # Test resource creation - result2 = resource_creation_function.remote("test-volume") - print("Resource creation result:", result2) - - # Test upload timeout handling - result3 = upload_with_timeout_handling.remote("local_file.txt", "/remote/path.txt") - print("Upload result:", result3) -``` - -### FilePatternMatcher - File Pattern Matching - -Pattern matching utility for filtering files based on glob patterns, similar to Docker's pattern matching with support for exclusion and complex patterns. - -```python { .api } -class FilePatternMatcher: - def __init__(self, *patterns: str) -> None: - """Create a file pattern matcher from glob patterns""" - - def __call__(self, path: Path) -> bool: - """Check if a path matches any of the patterns""" - - def __invert__(self) -> "FilePatternMatcher": - """Invert the matcher to exclude matching patterns""" - - def can_prune_directories(self) -> bool: - """Check if directory pruning is safe for optimization""" -``` - -#### Usage Examples - -```python -import modal -from pathlib import Path - -app = modal.App("pattern-matching") - -# Basic pattern matching -python_matcher = modal.FilePatternMatcher("*.py") -assert python_matcher(Path("script.py")) -assert not python_matcher(Path("README.md")) - -# Multiple patterns -code_matcher = modal.FilePatternMatcher("*.py", "*.js", "*.ts") -assert code_matcher(Path("app.js")) -assert code_matcher(Path("component.ts")) -assert not code_matcher(Path("config.json")) - -# Complex patterns with wildcards -deep_matcher = modal.FilePatternMatcher("**/*.py", "src/**/*.js") -assert deep_matcher(Path("deep/nested/script.py")) -assert deep_matcher(Path("src/components/app.js")) - -# Inverted patterns (exclusion) -non_python_matcher = ~modal.FilePatternMatcher("*.py") -assert not non_python_matcher(Path("script.py")) -assert non_python_matcher(Path("README.md")) - -@app.function() -def filter_files_function(file_paths: list[str]) -> dict: - """Function that filters files using pattern matching""" - - # Create different matchers for different file types - source_files = modal.FilePatternMatcher("*.py", "*.js", "*.ts", "*.java") - config_files = modal.FilePatternMatcher("*.json", "*.yaml", "*.yml", "*.toml") - doc_files = modal.FilePatternMatcher("*.md", "*.txt", "*.rst") - - # Exclude certain patterns - exclude_matcher = modal.FilePatternMatcher( - "**/__pycache__/**", - "**/node_modules/**", - "**/.git/**", - "*.pyc", - "*.tmp" - ) - - results = { - "source_files": [], - "config_files": [], - "doc_files": [], - "other_files": [], - "excluded_files": [] - } - - for file_path_str in file_paths: - file_path = Path(file_path_str) - - # Check if file should be excluded - if exclude_matcher(file_path): - results["excluded_files"].append(file_path_str) - continue - - # Categorize files by type - if source_files(file_path): - results["source_files"].append(file_path_str) - elif config_files(file_path): - results["config_files"].append(file_path_str) - elif doc_files(file_path): - results["doc_files"].append(file_path_str) - else: - results["other_files"].append(file_path_str) - - return results - -@app.function() -def advanced_pattern_matching(directory_paths: list[str]) -> dict: - """Advanced pattern matching with multiple criteria""" - - # Different matchers for different purposes - test_files = modal.FilePatternMatcher("**/test_*.py", "**/*_test.py", "**/tests/**/*.py") - build_artifacts = modal.FilePatternMatcher( - "**/build/**", - "**/dist/**", - "**/*.egg-info/**" - ) - hidden_files = modal.FilePatternMatcher(".*", "**/.*/**") - - # Inverted matchers for inclusion patterns - include_source = ~modal.FilePatternMatcher( - "**/__pycache__/**", - "**/.*/**", - "**/node_modules/**" - ) - - analysis = { - "test_files": [], - "build_artifacts": [], - "hidden_files": [], - "source_files": [], - "total_processed": 0 - } - - for dir_path_str in directory_paths: - dir_path = Path(dir_path_str) - - # Walk through directory structure - if dir_path.exists() and dir_path.is_dir(): - for file_path in dir_path.rglob("*"): - if file_path.is_file(): - analysis["total_processed"] += 1 - relative_path = file_path.relative_to(dir_path) - - # Categorize files - if test_files(relative_path): - analysis["test_files"].append(str(relative_path)) - elif build_artifacts(relative_path): - analysis["build_artifacts"].append(str(relative_path)) - elif hidden_files(relative_path): - analysis["hidden_files"].append(str(relative_path)) - elif include_source(relative_path): - analysis["source_files"].append(str(relative_path)) - - return analysis - -@app.function() -def custom_file_processing(): - """Demonstrate custom pattern matching logic""" - - # Create matcher for Python files, excluding tests - python_no_tests = modal.FilePatternMatcher("*.py") & ~modal.FilePatternMatcher("test_*.py", "*_test.py") - - # Process files with custom logic - files_to_process = [ - "app.py", - "test_app.py", - "utils.py", - "config_test.py", - "main.py" - ] - - results = [] - for filename in files_to_process: - file_path = Path(filename) - - if python_no_tests(file_path): - # Custom processing for source files - result = { - "file": filename, - "type": "source", - "processed": True, - "action": "analyze_code" - } - else: - result = { - "file": filename, - "type": "test" if "test" in filename else "other", - "processed": False, - "action": "skip" - } - - results.append(result) - - return results - -# Utility function for complex pattern combinations -def create_project_filter(): - """Create a comprehensive project file filter""" - - # Include source files - include_patterns = modal.FilePatternMatcher( - "**/*.py", - "**/*.js", - "**/*.ts", - "**/*.json", - "**/*.yaml", - "**/*.yml", - "**/*.md" - ) - - # Exclude build artifacts and temporary files - exclude_patterns = modal.FilePatternMatcher( - "**/__pycache__/**", - "**/node_modules/**", - "**/.git/**", - "**/build/**", - "**/dist/**", - "**/*.pyc", - "**/*.pyo", - "**/*.egg-info/**", - "**/coverage/**", - "**/.pytest_cache/**" - ) - - # Combine patterns: include source files but exclude artifacts - return lambda path: include_patterns(path) and not exclude_patterns(path) - -@app.local_entrypoint() -def main(): - # Test file filtering - sample_files = [ - "src/app.py", - "src/utils.py", - "tests/test_app.py", - "config.json", - "README.md", - "build/output.js", - "__pycache__/app.cpython-39.pyc", - "node_modules/package/index.js" - ] - - filter_result = filter_files_function.remote(sample_files) - print("File filtering result:", filter_result) - - # Test advanced pattern matching - directories = ["./src", "./tests", "./config"] - advanced_result = advanced_pattern_matching.remote(directories) - print("Advanced pattern matching:", advanced_result) - - # Test custom processing - custom_result = custom_file_processing.remote() - print("Custom processing result:", custom_result) -``` - -## Advanced Utility Patterns - -### Comprehensive Error Recovery System - -```python -import modal -import time -import random - -app = modal.App("error-recovery") - -class RetryableError(modal.Error): - """Custom error type for retryable operations""" - pass - -def with_error_recovery(max_retries: int = 3, base_delay: float = 1.0): - """Decorator for automatic error recovery""" - def decorator(func): - def wrapper(*args, **kwargs): - last_error = None - - for attempt in range(max_retries + 1): - try: - return func(*args, **kwargs) - - except modal.TimeoutError as e: - last_error = e - if attempt < max_retries: - delay = base_delay * (2 ** attempt) # Exponential backoff - print(f"Timeout on attempt {attempt + 1}, retrying in {delay}s") - time.sleep(delay) - else: - print(f"All {max_retries + 1} attempts failed due to timeout") - - except modal.ConnectionError as e: - last_error = e - if attempt < max_retries: - delay = base_delay * (1.5 ** attempt) - print(f"Connection error on attempt {attempt + 1}, retrying in {delay}s") - time.sleep(delay) - else: - print(f"All {max_retries + 1} attempts failed due to connection issues") - - except RetryableError as e: - last_error = e - if attempt < max_retries: - delay = base_delay + random.uniform(0, 1) # Jittered delay - print(f"Retryable error on attempt {attempt + 1}, retrying in {delay:.2f}s") - time.sleep(delay) - else: - print(f"All {max_retries + 1} attempts failed due to retryable errors") - - except modal.Error as e: - # Non-retryable Modal errors - print(f"Non-retryable Modal error: {type(e).__name__}: {e}") - raise - - except Exception as e: - # Unknown errors - print(f"Unknown error: {type(e).__name__}: {e}") - raise - - # All retries exhausted - raise last_error - - return wrapper - return decorator - -@app.function() -@with_error_recovery(max_retries=5, base_delay=2.0) -def resilient_operation(data: dict): - """Operation with comprehensive error recovery""" - - # Simulate various failure modes - failure_mode = data.get("failure_mode", "none") - - if failure_mode == "timeout": - raise modal.FunctionTimeoutError("Simulated timeout") - elif failure_mode == "connection": - raise modal.ConnectionError("Simulated connection error") - elif failure_mode == "retryable": - raise RetryableError("Simulated retryable error") - elif failure_mode == "auth": - raise modal.AuthError("Simulated auth error") # Non-retryable - - # Success case - return {"status": "success", "data": data} - -@app.local_entrypoint() -def main(): - # Test different error scenarios - test_cases = [ - {"data": {"value": 1}, "expected": "success"}, - {"data": {"value": 2, "failure_mode": "timeout"}, "expected": "retry_success"}, - {"data": {"value": 3, "failure_mode": "connection"}, "expected": "retry_success"}, - {"data": {"value": 4, "failure_mode": "retryable"}, "expected": "retry_success"}, - {"data": {"value": 5, "failure_mode": "auth"}, "expected": "immediate_failure"}, - ] - - for i, test_case in enumerate(test_cases): - print(f"\nTest case {i + 1}: {test_case['expected']}") - try: - result = resilient_operation.remote(test_case["data"]) - print(f"Result: {result}") - except Exception as e: - print(f"Final error: {type(e).__name__}: {e}") -``` - -### Intelligent File Processing Pipeline - -```python -import modal -from pathlib import Path -import json - -app = modal.App("file-processing-pipeline") - -class FileProcessor: - """Advanced file processing with pattern matching""" - - def __init__(self): - # Define processing rules based on file patterns - self.processors = { - "python": { - "matcher": modal.FilePatternMatcher("*.py"), - "action": self.process_python_file, - "priority": 1 - }, - "javascript": { - "matcher": modal.FilePatternMatcher("*.js", "*.ts"), - "action": self.process_js_file, - "priority": 1 - }, - "config": { - "matcher": modal.FilePatternMatcher("*.json", "*.yaml", "*.yml"), - "action": self.process_config_file, - "priority": 2 - }, - "documentation": { - "matcher": modal.FilePatternMatcher("*.md", "*.rst", "*.txt"), - "action": self.process_doc_file, - "priority": 3 - }, - "tests": { - "matcher": modal.FilePatternMatcher("**/test_*.py", "**/*_test.py", "**/tests/**/*.py"), - "action": self.process_test_file, - "priority": 0 # Highest priority - } - } - - # Files to exclude from processing - self.exclude_matcher = modal.FilePatternMatcher( - "**/__pycache__/**", - "**/node_modules/**", - "**/.git/**", - "**/venv/**", - "**/*.pyc" - ) - - def process_python_file(self, file_path: Path) -> dict: - """Process Python source files""" - return { - "type": "python", - "analysis": "syntax_check_passed", - "metrics": {"lines": 100, "functions": 5}, - "issues": [] - } - - def process_js_file(self, file_path: Path) -> dict: - """Process JavaScript/TypeScript files""" - return { - "type": "javascript", - "analysis": "linting_passed", - "metrics": {"lines": 75, "functions": 3}, - "issues": [] - } - - def process_config_file(self, file_path: Path) -> dict: - """Process configuration files""" - return { - "type": "config", - "analysis": "valid_format", - "validation": "schema_compliant", - "issues": [] - } - - def process_doc_file(self, file_path: Path) -> dict: - """Process documentation files""" - return { - "type": "documentation", - "analysis": "spell_check_passed", - "metrics": {"word_count": 500}, - "issues": [] - } - - def process_test_file(self, file_path: Path) -> dict: - """Process test files""" - return { - "type": "test", - "analysis": "test_discovery_complete", - "metrics": {"test_count": 10, "coverage": 85.5}, - "issues": [] - } - - def classify_file(self, file_path: Path) -> tuple[str, dict]: - """Classify file and return appropriate processor""" - # Check exclusions first - if self.exclude_matcher(file_path): - return "excluded", {} - - # Find matching processor with highest priority - matches = [] - for name, processor in self.processors.items(): - if processor["matcher"](file_path): - matches.append((processor["priority"], name, processor)) - - if matches: - # Sort by priority (lower number = higher priority) - matches.sort(key=lambda x: x[0]) - _, name, processor = matches[0] - return name, processor - - return "unknown", {} - -@app.function() -def intelligent_file_processing(file_paths: list[str]) -> dict: - """Process files using intelligent pattern matching""" - - processor = FileProcessor() - results = { - "processed": {}, - "excluded": [], - "unknown": [], - "errors": [], - "statistics": { - "total_files": len(file_paths), - "processed_count": 0, - "excluded_count": 0, - "unknown_count": 0, - "error_count": 0 - } - } - - for file_path_str in file_paths: - try: - file_path = Path(file_path_str) - file_type, processor_info = processor.classify_file(file_path) - - if file_type == "excluded": - results["excluded"].append(file_path_str) - results["statistics"]["excluded_count"] += 1 - - elif file_type == "unknown": - results["unknown"].append(file_path_str) - results["statistics"]["unknown_count"] += 1 - - else: - # Process the file - process_result = processor_info["action"](file_path) - results["processed"][file_path_str] = { - "file_type": file_type, - "priority": processor_info["priority"], - "result": process_result - } - results["statistics"]["processed_count"] += 1 - - except Exception as e: - error_info = { - "file": file_path_str, - "error": str(e), - "error_type": type(e).__name__ - } - results["errors"].append(error_info) - results["statistics"]["error_count"] += 1 - - return results - -@app.function() -def batch_file_analysis(directory_paths: list[str]) -> dict: - """Analyze entire directories with pattern-based filtering""" - - processor = FileProcessor() - analysis = { - "directories": {}, - "summary": { - "total_directories": len(directory_paths), - "total_files": 0, - "file_type_counts": {}, - "processing_time": 0 - } - } - - start_time = time.time() - - for dir_path_str in directory_paths: - dir_path = Path(dir_path_str) - dir_analysis = { - "files": [], - "file_counts": {}, - "excluded_files": [] - } - - if dir_path.exists() and dir_path.is_dir(): - for file_path in dir_path.rglob("*"): - if file_path.is_file(): - analysis["summary"]["total_files"] += 1 - relative_path = file_path.relative_to(dir_path) - - file_type, processor_info = processor.classify_file(relative_path) - - if file_type == "excluded": - dir_analysis["excluded_files"].append(str(relative_path)) - else: - dir_analysis["files"].append({ - "path": str(relative_path), - "type": file_type, - "priority": processor_info.get("priority", 999) - }) - - # Update counts - dir_analysis["file_counts"][file_type] = dir_analysis["file_counts"].get(file_type, 0) + 1 - analysis["summary"]["file_type_counts"][file_type] = analysis["summary"]["file_type_counts"].get(file_type, 0) + 1 - - analysis["directories"][dir_path_str] = dir_analysis - - analysis["summary"]["processing_time"] = time.time() - start_time - return analysis - -@app.local_entrypoint() -def main(): - # Test intelligent file processing - sample_files = [ - "src/main.py", - "src/utils.py", - "tests/test_main.py", - "config/settings.json", - "docs/README.md", - "package.json", - "node_modules/lib/index.js", # Should be excluded - "__pycache__/main.cpython-39.pyc", # Should be excluded - "data/sample.csv", # Unknown type - "Dockerfile" # Unknown type - ] - - processing_result = intelligent_file_processing.remote(sample_files) - print("Intelligent processing result:") - print(json.dumps(processing_result, indent=2)) - - # Test batch directory analysis - directories = ["./src", "./tests", "./config"] - analysis_result = batch_file_analysis.remote(directories) - print("\nBatch analysis result:") - print(json.dumps(analysis_result, indent=2)) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md b/.tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md deleted file mode 100644 index bee992136..000000000 --- a/.tessl/tiles/tessl/pypi-modal/docs/web-api-integration.md +++ /dev/null @@ -1,567 +0,0 @@ -# Web & API Integration - -Modal provides comprehensive web application serving capabilities, enabling deployment of web servers, REST APIs, and integration with popular web frameworks. These decorators transform Modal functions into web endpoints that can handle HTTP requests and serve web applications. - -## Capabilities - -### ASGI Application Support - -Decorator for serving ASGI-compatible applications like FastAPI, Starlette, and modern async web frameworks. - -```python { .api } -def asgi_app(func: Callable) -> Callable: - """Decorator to serve ASGI applications (FastAPI, Starlette, etc.)""" -``` - -#### Usage Examples - -```python -import modal -from fastapi import FastAPI - -app = modal.App("fastapi-server") -web_app = FastAPI() - -@web_app.get("/") -def read_root(): - return {"message": "Hello from Modal FastAPI!"} - -@web_app.get("/items/{item_id}") -def read_item(item_id: int, q: str = None): - return {"item_id": item_id, "q": q} - -@web_app.post("/process") -def process_data(data: dict): - # Expensive processing that benefits from Modal's scaling - result = expensive_computation(data) - return {"result": result} - -@app.function() -@modal.asgi_app() -def fastapi_app(): - return web_app - -# The FastAPI app is now served on Modal's infrastructure -``` - -### WSGI Application Support - -Decorator for serving WSGI-compatible applications like Flask, Django, and traditional web frameworks. - -```python { .api } -def wsgi_app(func: Callable) -> Callable: - """Decorator to serve WSGI applications (Flask, Django, etc.)""" -``` - -#### Usage Examples - -```python -import modal -from flask import Flask, request, jsonify - -app = modal.App("flask-server") -web_app = Flask(__name__) - -@web_app.route("/") -def hello(): - return jsonify({"message": "Hello from Modal Flask!"}) - -@web_app.route("/upload", methods=["POST"]) -def upload_file(): - if 'file' not in request.files: - return jsonify({"error": "No file uploaded"}), 400 - - file = request.files['file'] - # Process file with Modal's compute resources - result = process_uploaded_file(file) - return jsonify({"processed": result}) - -@web_app.route("/predict", methods=["POST"]) -def make_prediction(): - data = request.get_json() - # Use Modal for ML inference - prediction = run_ml_model(data) - return jsonify({"prediction": prediction}) - -@app.function() -@modal.wsgi_app() -def flask_app(): - return web_app - -# The Flask app is now served on Modal's infrastructure -``` - -### Web Endpoint - -Decorator for creating simple HTTP endpoints without requiring a full web framework. - -```python { .api } -def web_endpoint(func: Callable) -> Callable: - """Decorator to create HTTP web endpoints""" -``` - -#### Usage Examples - -```python -import modal -import json - -app = modal.App("simple-api") - -@app.function() -@modal.web_endpoint(method="GET") -def get_status(): - """Simple GET endpoint""" - return { - "status": "healthy", - "timestamp": time.time(), - "version": "1.0.0" - } - -@app.function() -@modal.web_endpoint(method="POST") -def process_webhook(data=None): - """Handle webhook POST requests""" - if data: - # Process webhook data - result = handle_webhook_data(data) - return {"success": True, "processed": result} - else: - return {"error": "No data provided"}, 400 - -@app.function() -@modal.web_endpoint(method="GET", path="/metrics") -def get_metrics(): - """Custom metrics endpoint""" - metrics = collect_application_metrics() - return {"metrics": metrics} - -# Endpoints are available at: -# GET https://your-app.modal.run/get_status -# POST https://your-app.modal.run/process_webhook -# GET https://your-app.modal.run/metrics -``` - -### FastAPI Endpoint - -Specialized decorator optimized for FastAPI applications with enhanced integration features. - -```python { .api } -def fastapi_endpoint(func: Callable) -> Callable: - """Decorator specifically for FastAPI endpoints with enhanced features""" -``` - -#### Usage Examples - -```python -import modal -from fastapi import FastAPI, HTTPException, Depends -from pydantic import BaseModel - -app = modal.App("advanced-fastapi") - -class PredictionRequest(BaseModel): - text: str - model: str = "default" - -class PredictionResponse(BaseModel): - prediction: str - confidence: float - model_used: str - -@app.function() -@modal.fastapi_endpoint() -def ml_prediction_api(): - """Advanced FastAPI endpoint with automatic docs and validation""" - fastapi_app = FastAPI( - title="ML Prediction API", - description="Machine learning predictions powered by Modal", - version="1.0.0" - ) - - @fastapi_app.post("/predict", response_model=PredictionResponse) - async def predict(request: PredictionRequest): - try: - # Load model and make prediction - model = load_ml_model(request.model) - prediction, confidence = model.predict(request.text) - - return PredictionResponse( - prediction=prediction, - confidence=confidence, - model_used=request.model - ) - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) - - @fastapi_app.get("/models") - async def list_models(): - """List available models""" - models = get_available_models() - return {"models": models} - - return fastapi_app - -# Automatic OpenAPI docs available at: -# https://your-app.modal.run/docs -# https://your-app.modal.run/redoc -``` - -### Web Server - -Decorator for creating custom web servers with full control over the HTTP handling. - -```python { .api } -def web_server(func: Callable) -> Callable: - """Decorator to create custom web servers""" -``` - -#### Usage Examples - -```python -import modal -from http.server import HTTPServer, BaseHTTPRequestHandler -import json - -app = modal.App("custom-server") - -class CustomHandler(BaseHTTPRequestHandler): - def do_GET(self): - if self.path == "/health": - self.send_response(200) - self.send_header('Content-type', 'application/json') - self.end_headers() - response = {"status": "healthy", "server": "custom"} - self.wfile.write(json.dumps(response).encode()) - else: - self.send_response(404) - self.end_headers() - - def do_POST(self): - if self.path == "/process": - content_length = int(self.headers['Content-Length']) - post_data = self.rfile.read(content_length) - - try: - data = json.loads(post_data.decode('utf-8')) - result = custom_processing(data) - - self.send_response(200) - self.send_header('Content-type', 'application/json') - self.end_headers() - self.wfile.write(json.dumps({"result": result}).encode()) - except Exception as e: - self.send_response(500) - self.end_headers() - -@app.function() -@modal.web_server() -def custom_web_server(): - """Custom HTTP server with full control""" - server = HTTPServer(("0.0.0.0", 8000), CustomHandler) - server.serve_forever() -``` - -## Advanced Web Patterns - -### Multi-Framework API Gateway - -```python -import modal -from fastapi import FastAPI -from flask import Flask -import asyncio - -app = modal.App("api-gateway") - -# FastAPI for async endpoints -fastapi_app = FastAPI() - -@fastapi_app.get("/async-data") -async def get_async_data(): - # Async data processing - data = await fetch_external_data() - return {"data": data, "type": "async"} - -# Flask for sync endpoints -flask_app = Flask(__name__) - -@flask_app.route("/sync-data") -def get_sync_data(): - # Synchronous data processing - data = fetch_local_data() - return {"data": data, "type": "sync"} - -@app.function() -@modal.asgi_app() -def async_api(): - return fastapi_app - -@app.function() -@modal.wsgi_app() -def sync_api(): - return flask_app - -# Different apps serve different endpoints -# https://your-app-async.modal.run/async-data -# https://your-app-sync.modal.run/sync-data -``` - -### Microservices Architecture - -```python -import modal -from fastapi import FastAPI - -app = modal.App("microservices") - -# User service -user_app = FastAPI(title="User Service") - -@user_app.get("/users/{user_id}") -def get_user(user_id: int): - return get_user_from_database(user_id) - -@user_app.post("/users") -def create_user(user_data: dict): - return create_user_in_database(user_data) - -# Order service -order_app = FastAPI(title="Order Service") - -@order_app.get("/orders/{order_id}") -def get_order(order_id: int): - return get_order_from_database(order_id) - -@order_app.post("/orders") -def create_order(order_data: dict): - # Validate user exists - user_id = order_data.get("user_id") - user = get_user_from_database(user_id) - if not user: - raise HTTPException(status_code=404, detail="User not found") - - return create_order_in_database(order_data) - -# Deploy as separate services -@app.function() -@modal.asgi_app() -def user_service(): - return user_app - -@app.function() -@modal.asgi_app() -def order_service(): - return order_app -``` - -### WebSocket and Real-time Features - -```python -import modal -from fastapi import FastAPI, WebSocket -from typing import List - -app = modal.App("realtime-app") -web_app = FastAPI() - -class ConnectionManager: - def __init__(self): - self.active_connections: List[WebSocket] = [] - - async def connect(self, websocket: WebSocket): - await websocket.accept() - self.active_connections.append(websocket) - - def disconnect(self, websocket: WebSocket): - self.active_connections.remove(websocket) - - async def broadcast(self, message: str): - for connection in self.active_connections: - await connection.send_text(message) - -manager = ConnectionManager() - -@web_app.websocket("/ws") -async def websocket_endpoint(websocket: WebSocket): - await manager.connect(websocket) - try: - while True: - data = await websocket.receive_text() - # Process message and broadcast to all connections - processed = process_realtime_data(data) - await manager.broadcast(f"Processed: {processed}") - except Exception: - manager.disconnect(websocket) - -@web_app.post("/broadcast") -def broadcast_message(message: dict): - """HTTP endpoint to broadcast messages""" - asyncio.create_task(manager.broadcast(json.dumps(message))) - return {"status": "broadcasted"} - -@app.function() -@modal.asgi_app() -def realtime_app(): - return web_app -``` - -### File Upload and Processing API - -```python -import modal -from fastapi import FastAPI, File, UploadFile, HTTPException -from typing import List -import tempfile -import os - -app = modal.App("file-processor") -web_app = FastAPI(title="File Processing API") - -@web_app.post("/upload/single") -async def upload_single_file(file: UploadFile = File(...)): - """Upload and process a single file""" - if not file.filename.endswith(('.jpg', '.png', '.pdf', '.txt')): - raise HTTPException(status_code=400, detail="Unsupported file type") - - # Save file temporarily - with tempfile.NamedTemporaryFile(delete=False) as tmp_file: - content = await file.read() - tmp_file.write(content) - tmp_path = tmp_file.name - - try: - # Process file with Modal compute - if file.filename.endswith(('.jpg', '.png')): - result = process_image(tmp_path) - elif file.filename.endswith('.pdf'): - result = extract_pdf_text(tmp_path) - else: - result = process_text_file(tmp_path) - - return { - "filename": file.filename, - "size": len(content), - "processed": result - } - finally: - os.unlink(tmp_path) - -@web_app.post("/upload/batch") -async def upload_multiple_files(files: List[UploadFile] = File(...)): - """Upload and process multiple files""" - results = [] - - for file in files: - try: - # Process each file - content = await file.read() - result = await process_file_async(file.filename, content) - results.append({ - "filename": file.filename, - "status": "success", - "result": result - }) - except Exception as e: - results.append({ - "filename": file.filename, - "status": "error", - "error": str(e) - }) - - return {"processed_files": results} - -@web_app.get("/download/{file_id}") -async def download_processed_file(file_id: str): - """Download processed file results""" - file_path = get_processed_file_path(file_id) - if not os.path.exists(file_path): - raise HTTPException(status_code=404, detail="File not found") - - return FileResponse(file_path) - -@app.function() -@modal.asgi_app() -def file_processing_api(): - return web_app -``` - -### Authentication and Middleware Integration - -```python -import modal -from fastapi import FastAPI, Depends, HTTPException, status -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials -from fastapi.middleware.cors import CORSMiddleware -import jwt - -app = modal.App("secure-api") -web_app = FastAPI(title="Secure API with Authentication") - -# Add CORS middleware -web_app.add_middleware( - CORSMiddleware, - allow_origins=["*"], # Configure appropriately for production - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -security = HTTPBearer() - -def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)): - """Verify JWT token""" - try: - payload = jwt.decode( - credentials.credentials, - os.environ["JWT_SECRET"], - algorithms=["HS256"] - ) - return payload - except jwt.ExpiredSignatureError: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Token expired" - ) - except jwt.InvalidTokenError: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid token" - ) - -@web_app.post("/login") -def login(credentials: dict): - """Authenticate user and return JWT token""" - if authenticate_user(credentials["username"], credentials["password"]): - token = create_jwt_token(credentials["username"]) - return {"access_token": token, "token_type": "bearer"} - else: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid credentials" - ) - -@web_app.get("/protected") -def protected_endpoint(current_user: dict = Depends(verify_token)): - """Protected endpoint requiring authentication""" - return { - "message": f"Hello {current_user['username']}!", - "data": get_user_specific_data(current_user['user_id']) - } - -@web_app.get("/admin") -def admin_endpoint(current_user: dict = Depends(verify_token)): - """Admin-only endpoint""" - if not current_user.get("is_admin"): - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="Admin access required" - ) - - return {"admin_data": get_admin_data()} - -@app.function(secrets=[modal.Secret.from_name("jwt-secret")]) -@modal.asgi_app() -def secure_api(): - return web_app -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-modal/tile.json b/.tessl/tiles/tessl/pypi-modal/tile.json deleted file mode 100644 index 1b7cf22dc..000000000 --- a/.tessl/tiles/tessl/pypi-modal/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-modal", - "version": "1.1.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/modal@1.1.4", - "summary": "Python client library for Modal, a serverless cloud computing platform that enables developers to run Python code in the cloud with on-demand access to compute resources." -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-openai/docs/assistants.md b/.tessl/tiles/tessl/pypi-openai/docs/assistants.md deleted file mode 100644 index c1dc5c0ab..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/assistants.md +++ /dev/null @@ -1,540 +0,0 @@ -# Assistants API - -Build AI assistants with advanced capabilities including code interpretation, file search, and function calling. Assistants maintain conversation state and can access tools to help users accomplish tasks. - -## Capabilities - -### Create Assistant - -Create an AI assistant with specific instructions and tools. - -```python { .api } -def create( - self, - *, - model: str, - description: str | Omit = omit, - instructions: str | Omit = omit, - metadata: dict[str, str] | Omit = omit, - name: str | Omit = omit, - reasoning_effort: str | Omit = omit, - response_format: dict | Omit = omit, - temperature: float | Omit = omit, - tool_resources: dict | Omit = omit, - tools: list[dict] | Omit = omit, - top_p: float | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Assistant: - """ - Create an AI assistant. - - Args: - model: Model ID to use (e.g., "gpt-4", "gpt-3.5-turbo"). - - description: Description of the assistant (max 512 chars). - - instructions: System instructions that guide assistant behavior (max 256k chars). - - metadata: Key-value pairs for storing additional info (max 16). - Keys max 64 chars, values max 512 chars. - - name: Name of the assistant (max 256 chars). - - reasoning_effort: Constrains effort on reasoning for reasoning models. - Currently supported values are "none", "minimal", "low", "medium", and "high". - Reducing reasoning effort can result in faster responses and fewer tokens used. - - gpt-5.1 defaults to "none" (no reasoning). Supports: none, low, medium, high. - - Models before gpt-5.1 default to "medium" and do not support "none". - - gpt-5-pro defaults to (and only supports) "high" reasoning effort. - - response_format: Output format specification. - - {"type": "text"}: Plain text (default) - - {"type": "json_object"}: Valid JSON - - {"type": "json_schema", "json_schema": {...}}: Structured output - - temperature: Sampling temperature between 0 and 2. Default 1. - - tool_resources: Resources for tools. Options: - - {"code_interpreter": {"file_ids": [...]}} - - {"file_search": {"vector_store_ids": [...]}} - - tools: List of enabled tools (maximum of 128 tools per assistant). Options: - - {"type": "code_interpreter"}: Python code execution - - {"type": "file_search"}: Search uploaded files - - {"type": "function", "function": {...}}: Custom functions - - top_p: Nucleus sampling parameter between 0 and 1. Default 1. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Assistant: Created assistant. - - Raises: - BadRequestError: Invalid parameters - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic assistant -assistant = client.beta.assistants.create( - name="Math Tutor", - instructions="You are a helpful math tutor. Explain concepts clearly.", - model="gpt-4" -) - -print(f"Assistant ID: {assistant.id}") - -# With code interpreter -assistant = client.beta.assistants.create( - name="Data Analyst", - instructions="Analyze data and create visualizations.", - model="gpt-4", - tools=[{"type": "code_interpreter"}] -) - -# With file search -assistant = client.beta.assistants.create( - name="Documentation Helper", - instructions="Help users find information in documentation.", - model="gpt-4", - tools=[{"type": "file_search"}], - tool_resources={ - "file_search": { - "vector_store_ids": ["vs_abc123"] - } - } -) - -# With function calling -tools = [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get current weather", - "parameters": { - "type": "object", - "properties": { - "location": {"type": "string"} - }, - "required": ["location"] - } - } - } -] - -assistant = client.beta.assistants.create( - name="Weather Assistant", - instructions="Help users check weather.", - model="gpt-4", - tools=tools -) - -# With structured output -assistant = client.beta.assistants.create( - name="Structured Assistant", - instructions="Generate structured responses.", - model="gpt-4", - response_format={ - "type": "json_schema", - "json_schema": { - "name": "response", - "strict": True, - "schema": { - "type": "object", - "properties": { - "answer": {"type": "string"} - }, - "required": ["answer"], - "additionalProperties": False - } - } - } -) - -# With metadata -assistant = client.beta.assistants.create( - name="Customer Support", - instructions="Assist customers with inquiries.", - model="gpt-4", - metadata={ - "department": "support", - "version": "1.0" - } -) - -# With reasoning effort (for reasoning models) -assistant = client.beta.assistants.create( - name="Reasoning Assistant", - instructions="Solve complex problems with reasoning.", - model="gpt-5.1", - reasoning_effort="high" -) -``` - -### Retrieve Assistant - -Get assistant details. - -```python { .api } -def retrieve( - self, - assistant_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Assistant: - """ - Retrieve assistant details. - - Args: - assistant_id: The ID of the assistant. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Assistant: Assistant details. - - Raises: - NotFoundError: Assistant not found - """ -``` - -Usage example: - -```python -assistant = client.beta.assistants.retrieve("asst_abc123") - -print(f"Name: {assistant.name}") -print(f"Model: {assistant.model}") -print(f"Tools: {assistant.tools}") -``` - -### Update Assistant - -Modify assistant properties. - -```python { .api } -def update( - self, - assistant_id: str, - *, - description: str | Omit = omit, - instructions: str | Omit = omit, - metadata: dict[str, str] | Omit = omit, - model: str | Omit = omit, - name: str | Omit = omit, - reasoning_effort: str | Omit = omit, - response_format: dict | Omit = omit, - temperature: float | Omit = omit, - tool_resources: dict | Omit = omit, - tools: list[dict] | Omit = omit, - top_p: float | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Assistant: - """ - Update assistant properties. - - Args: Same as create method, but all optional. - - Returns: - Assistant: Updated assistant. - """ -``` - -Usage example: - -```python -# Update instructions -assistant = client.beta.assistants.update( - "asst_abc123", - instructions="Updated instructions." -) - -# Add tools -assistant = client.beta.assistants.update( - "asst_abc123", - tools=[ - {"type": "code_interpreter"}, - {"type": "file_search"} - ] -) -``` - -### List Assistants - -List all assistants with pagination. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Assistant]: - """ - List assistants with pagination. - - Args: - after: Cursor for next page. - before: Cursor for previous page. - limit: Number to retrieve (max 100). Default 20. - order: Sort order. "asc" or "desc". Default "desc". - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[Assistant]: Paginated list of assistants. - """ -``` - -Usage example: - -```python -# List all assistants -assistants = client.beta.assistants.list() - -for assistant in assistants: - print(f"{assistant.name} ({assistant.id})") - -# Pagination -page1 = client.beta.assistants.list(limit=10) -page2 = client.beta.assistants.list(limit=10, after=page1.data[-1].id) -``` - -### Delete Assistant - -Delete an assistant. - -```python { .api } -def delete( - self, - assistant_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> AssistantDeleted: - """ - Delete an assistant. - - Args: - assistant_id: The ID of the assistant to delete. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - AssistantDeleted: Deletion confirmation. - - Raises: - NotFoundError: Assistant not found - """ -``` - -Usage example: - -```python -result = client.beta.assistants.delete("asst_abc123") - -print(f"Deleted: {result.deleted}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Assistant(BaseModel): - """AI Assistant.""" - id: str - created_at: int - description: str | None - instructions: str | None - metadata: dict[str, str] | None - model: str - name: str | None - object: Literal["assistant"] - tools: list[dict] - response_format: dict | None - temperature: float | None - tool_resources: dict | None - top_p: float | None - -class AssistantDeleted(BaseModel): - """Deletion confirmation.""" - id: str - deleted: bool - object: Literal["assistant.deleted"] - -# Tool types -class CodeInterpreterTool(TypedDict): - type: Literal["code_interpreter"] - -class FileSearchTool(TypedDict): - type: Literal["file_search"] - -class FunctionTool(TypedDict): - type: Literal["function"] - function: FunctionDefinition - -class FunctionDefinition(TypedDict): - name: str - description: NotRequired[str] - parameters: dict # JSON Schema - strict: NotRequired[bool] - -# Tool resources -class ToolResources(TypedDict, total=False): - code_interpreter: CodeInterpreterResources - file_search: FileSearchResources - -class CodeInterpreterResources(TypedDict, total=False): - file_ids: list[str] - -class FileSearchResources(TypedDict, total=False): - vector_store_ids: list[str] -``` - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Create specialized assistants -support_assistant = client.beta.assistants.create( - name="Customer Support", - instructions="Friendly and helpful support agent.", - model="gpt-4", - tools=[{"type": "file_search"}] -) - -code_assistant = client.beta.assistants.create( - name="Code Helper", - instructions="Help with coding questions.", - model="gpt-4", - tools=[{"type": "code_interpreter"}] -) - -# 2. Organize with metadata -assistant = client.beta.assistants.create( - name="Sales Bot", - model="gpt-4", - metadata={ - "team": "sales", - "region": "us-west", - "version": "2.0" - } -) - -# 3. Update as needed -assistant = client.beta.assistants.update( - assistant.id, - instructions="Updated instructions based on feedback." -) - -# 4. Clean up unused assistants -assistants = client.beta.assistants.list() -for assistant in assistants: - if should_delete(assistant): - client.beta.assistants.delete(assistant.id) -``` - -## Complete Example - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Create assistant -assistant = client.beta.assistants.create( - name="Math Tutor", - instructions="You are a helpful math tutor.", - model="gpt-4", - tools=[{"type": "code_interpreter"}] -) - -# 2. Create thread -thread = client.beta.threads.create() - -# 3. Add message -message = client.beta.threads.messages.create( - thread_id=thread.id, - role="user", - content="Can you help me solve x^2 + 5x + 6 = 0?" -) - -# 4. Run assistant -run = client.beta.threads.runs.create( - thread_id=thread.id, - assistant_id=assistant.id -) - -# 5. Wait for completion -import time - -while run.status not in ["completed", "failed"]: - time.sleep(1) - run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) - -# 6. Get response -messages = client.beta.threads.messages.list(thread_id=thread.id) - -for message in messages.data: - if message.role == "assistant": - print(message.content[0].text.value) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_assistant(): - client = AsyncOpenAI() - - assistant = await client.beta.assistants.create( - name="Async Assistant", - model="gpt-4", - instructions="I help async." - ) - - return assistant.id - -assistant_id = asyncio.run(create_assistant()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/audio.md b/.tessl/tiles/tessl/pypi-openai/docs/audio.md deleted file mode 100644 index 863376385..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/audio.md +++ /dev/null @@ -1,579 +0,0 @@ -# Audio - -Convert audio to text (transcription and translation) and text to speech using Whisper and TTS models. Supports multiple audio formats and languages. - -## Capabilities - -### Transcription - -Convert audio to text in the original language using the Whisper model. - -```python { .api } -def create( - self, - *, - file: FileTypes, - model: str | AudioModel, - chunking_strategy: dict | str | Omit = omit, - include: list[str] | Omit = omit, - known_speaker_names: list[str] | Omit = omit, - known_speaker_references: list[str] | Omit = omit, - language: str | Omit = omit, - prompt: str | Omit = omit, - response_format: Literal["json", "text", "srt", "verbose_json", "vtt", "diarized_json"] | Omit = omit, - stream: bool | Omit = omit, - temperature: float | Omit = omit, - timestamp_granularities: list[Literal["word", "segment"]] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Transcription | TranscriptionVerbose: - """ - Transcribe audio to text in the original language. - - Args: - file: Audio file to transcribe. Supported formats: flac, mp3, mp4, mpeg, - mpga, m4a, ogg, wav, webm. Max file size: 25 MB. - Can be file path string, file object, or tuple. - - model: Model ID. Options: - - "gpt-4o-transcribe": Advanced transcription with streaming support - - "gpt-4o-mini-transcribe": Faster, cost-effective transcription - - "gpt-4o-transcribe-diarize": Speaker diarization model - - "whisper-1": Powered by open source Whisper V2 model - - chunking_strategy: Controls how audio is cut into chunks. Options: - - "auto": Server normalizes loudness and uses voice activity detection (VAD) - - {"type": "server_vad", ...}: Manually configure VAD parameters - - If unset: Audio transcribed as a single block - - Required for gpt-4o-transcribe-diarize with inputs >30 seconds - - include: Additional information to include. Options: - - "logprobs": Returns log probabilities for confidence analysis - - Only works with response_format="json" - - Only supported for gpt-4o-transcribe and gpt-4o-mini-transcribe - - Not supported with gpt-4o-transcribe-diarize - - known_speaker_names: List of speaker names for diarization (e.g., ["customer", "agent"]). - Corresponds to audio samples in known_speaker_references. Up to 4 speakers. - Used with gpt-4o-transcribe-diarize model. - - known_speaker_references: List of audio samples (as data URLs) containing known speaker - references. Each sample must be 2-10 seconds. Matches known_speaker_names. - Used with gpt-4o-transcribe-diarize model. - - language: Language of the audio in ISO-639-1 format (e.g., "en", "fr", "de"). - Providing language improves accuracy and latency. - - prompt: Optional text to guide the model's style or continue previous segment. - Should match audio language. - - response_format: Output format. Options: - - "json": JSON with text (default) - - "text": Plain text only - - "srt": SubRip subtitle format - - "verbose_json": JSON with segments, timestamps, confidence - - "vtt": WebVTT subtitle format - - "diarized_json": JSON with speaker annotations (for gpt-4o-transcribe-diarize) - Note: gpt-4o-transcribe/mini only support "json". gpt-4o-transcribe-diarize - supports "json", "text", and "diarized_json" (required for speaker annotations). - - stream: If true, model response will be streamed using server-sent events. - Returns Stream[TranscriptionStreamEvent]. Not supported for whisper-1. - - temperature: Sampling temperature between 0 and 1. Higher values increase - randomness. Default is 0. - - timestamp_granularities: Timestamp precision options. - - ["segment"]: Segment-level timestamps (default) - - ["word"]: Word-level timestamps - - ["segment", "word"]: Both levels - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Transcription: Basic response with text - TranscriptionVerbose: Detailed response with segments and timestamps - - Raises: - BadRequestError: Invalid file format or size - AuthenticationError: Invalid API key - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic transcription -with open("audio.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="whisper-1", - file=audio_file - ) -print(transcript.text) - -# With language hint for better accuracy -with open("french_audio.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="whisper-1", - file=audio_file, - language="fr" - ) - -# Verbose JSON with detailed information -with open("audio.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="whisper-1", - file=audio_file, - response_format="verbose_json", - timestamp_granularities=["word", "segment"] - ) - -print(f"Duration: {transcript.duration}") -print(f"Language: {transcript.language}") - -for segment in transcript.segments: - print(f"[{segment.start:.2f}s - {segment.end:.2f}s]: {segment.text}") - -for word in transcript.words: - print(f"{word.word} ({word.start:.2f}s)") - -# SRT subtitle format -with open("video_audio.mp3", "rb") as audio_file: - srt = client.audio.transcriptions.create( - model="whisper-1", - file=audio_file, - response_format="srt" - ) -# Save to file -with open("subtitles.srt", "w") as f: - f.write(srt) - -# With prompt for context/style -with open("continuation.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="whisper-1", - file=audio_file, - prompt="Previous text for context..." - ) - -# Using file_from_path helper -from openai import OpenAI -from openai import file_from_path - -client = OpenAI() - -transcript = client.audio.transcriptions.create( - model="whisper-1", - file=file_from_path("audio.mp3") -) - -# Advanced: Using gpt-4o-transcribe with streaming -with open("audio.mp3", "rb") as audio_file: - stream = client.audio.transcriptions.create( - model="gpt-4o-transcribe", - file=audio_file, - stream=True - ) - for event in stream: - print(event.text, end="", flush=True) - -# Advanced: Speaker diarization with gpt-4o-transcribe-diarize -with open("meeting.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="gpt-4o-transcribe-diarize", - file=audio_file, - response_format="diarized_json", - chunking_strategy="auto" - ) - for segment in transcript.segments: - print(f"[{segment.speaker}]: {segment.text}") - -# Advanced: With known speaker references -with open("call.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="gpt-4o-transcribe-diarize", - file=audio_file, - response_format="diarized_json", - known_speaker_names=["customer", "agent"], - known_speaker_references=[ - "data:audio/mp3;base64,...", # Customer voice sample - "data:audio/mp3;base64,..." # Agent voice sample - ] - ) - -# Advanced: Using include for confidence scores -with open("audio.mp3", "rb") as audio_file: - transcript = client.audio.transcriptions.create( - model="gpt-4o-transcribe", - file=audio_file, - response_format="json", - include=["logprobs"] - ) - # Access logprobs for confidence analysis -``` - -### Translation - -Translate audio to English text using the Whisper model. - -```python { .api } -def create( - self, - *, - file: FileTypes, - model: str | AudioModel, - prompt: str | Omit = omit, - response_format: Literal["json", "text", "srt", "verbose_json", "vtt"] | Omit = omit, - temperature: float | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Translation | TranslationVerbose | str: - """ - Translate audio to English text. - - Args: - file: Audio file to translate. Supported formats: flac, mp3, mp4, mpeg, - mpga, m4a, ogg, wav, webm. Max file size: 25 MB. - - model: Model ID. Currently only "whisper-1" is available. - - prompt: Optional text to guide the model's style. - - response_format: Output format. Options: - - "json": JSON with text (default) - - "text": Plain text only - - "srt": SubRip subtitle format - - "verbose_json": JSON with segments and details - - "vtt": WebVTT subtitle format - - temperature: Sampling temperature between 0 and 1. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Translation: Basic response with English text (for json format) - TranslationVerbose: Detailed response with segments (for verbose_json format) - str: Plain text string (for text, srt, vtt formats) - - Raises: - BadRequestError: Invalid file format or size - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Translate French audio to English -with open("french_audio.mp3", "rb") as audio_file: - translation = client.audio.translations.create( - model="whisper-1", - file=audio_file - ) -print(translation.text) - -# Verbose format with segments -with open("spanish_audio.mp3", "rb") as audio_file: - translation = client.audio.translations.create( - model="whisper-1", - file=audio_file, - response_format="verbose_json" - ) - -for segment in translation.segments: - print(f"[{segment.start:.2f}s]: {segment.text}") -``` - -### Text-to-Speech - -Convert text to spoken audio using TTS models. - -```python { .api } -def create( - self, - *, - input: str, - model: str | SpeechModel, - voice: Literal["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"], - instructions: str | Omit = omit, - response_format: Literal["mp3", "opus", "aac", "flac", "wav", "pcm"] | Omit = omit, - speed: float | Omit = omit, - stream_format: Literal["sse", "audio"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> HttpxBinaryResponseContent: - """ - Convert text to spoken audio. - - Args: - input: Text to convert to audio. Max length: 4096 characters. - - model: TTS model to use. Options: - - "tts-1": Standard quality, faster, lower cost - - "tts-1-hd": High definition quality, slower, higher cost - - "gpt-4o-mini-tts": Advanced model with instruction support - - voice: Voice to use for generation. Options: - - "alloy": Neutral, balanced - - "ash": Clear and articulate - - "ballad": Warm and expressive - - "coral": Bright and engaging - - "echo": Calm and measured - - "sage": Wise and authoritative - - "shimmer": Soft and gentle - - "verse": Dynamic and versatile - - "marin": Smooth and professional - - "cedar": Rich and grounded - - instructions: Control the voice with additional instructions. - Does not work with tts-1 or tts-1-hd. Only supported by gpt-4o-mini-tts. - - response_format: Audio format. Options: - - "mp3": Default, good compression - - "opus": Best for streaming, lower latency - - "aac": Good compression, widely supported - - "flac": Lossless compression - - "wav": Uncompressed - - "pcm": Raw 16-bit PCM audio - - speed: Playback speed between 0.25 and 4.0. Default 1.0. - - stream_format: Format to stream the audio in. Options: - - "sse": Server-sent events streaming - - "audio": Raw audio streaming - Note: "sse" not supported for tts-1 or tts-1-hd. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - HttpxBinaryResponseContent: Audio file content. Use .content for bytes, - .read() for streaming, .stream_to_file(path) for direct save. - - Raises: - BadRequestError: Invalid parameters or text too long - """ -``` - -Usage examples: - -```python -from openai import OpenAI -from pathlib import Path - -client = OpenAI() - -# Basic TTS -response = client.audio.speech.create( - model="tts-1", - voice="alloy", - input="Hello! This is a test of text to speech." -) - -# Save to file -speech_file = Path("output.mp3") -response.stream_to_file(speech_file) - -# Different voices -voices = ["alloy", "ash", "ballad", "coral", "echo", "sage", "shimmer", "verse", "marin", "cedar"] -for voice in voices: - response = client.audio.speech.create( - model="tts-1", - voice=voice, - input="Testing different voices." - ) - response.stream_to_file(f"voice_{voice}.mp3") - -# High quality audio (using marin or cedar recommended for best quality) -response = client.audio.speech.create( - model="tts-1-hd", - voice="marin", - input="High definition audio output." -) -response.stream_to_file("hd_output.mp3") - -# Streaming optimized format (Opus) -response = client.audio.speech.create( - model="tts-1", - voice="shimmer", - input="Optimized for streaming.", - response_format="opus" -) -response.stream_to_file("output.opus") - -# Adjust playback speed -response = client.audio.speech.create( - model="tts-1", - voice="alloy", - input="This will play faster.", - speed=1.5 -) -response.stream_to_file("fast_speech.mp3") - -# Get raw bytes -response = client.audio.speech.create( - model="tts-1", - voice="echo", - input="Getting raw audio bytes." -) -audio_bytes = response.content -# Process bytes as needed - -# Streaming response -response = client.audio.speech.create( - model="tts-1", - voice="ballad", - input="Streaming audio data." -) - -with open("streaming.mp3", "wb") as f: - for chunk in response.iter_bytes(): - f.write(chunk) - -# Advanced: Using gpt-4o-mini-tts with instructions -response = client.audio.speech.create( - model="gpt-4o-mini-tts", - voice="sage", - input="This is a test of voice control.", - instructions="Speak in a warm, friendly tone with slight enthusiasm." -) -response.stream_to_file("instructed_speech.mp3") - -# Advanced: Server-sent events streaming -response = client.audio.speech.create( - model="gpt-4o-mini-tts", - voice="coral", - input="Real-time audio streaming.", - stream_format="sse" -) -response.stream_to_file("sse_output.mp3") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -# Transcription types -class Transcription(BaseModel): - text: str - -class TranscriptionVerbose(BaseModel): - text: str - language: str - duration: float - segments: list[TranscriptionSegment] | None - words: list[TranscriptionWord] | None - -class TranscriptionSegment(BaseModel): - id: int - seek: int - start: float - end: float - text: str - tokens: list[int] - temperature: float - avg_logprob: float - compression_ratio: float - no_speech_prob: float - -class TranscriptionWord(BaseModel): - word: str - start: float - end: float - -class TranscriptionDiarized(BaseModel): - """Transcription with speaker diarization.""" - text: str - language: str - duration: float - segments: list[DiarizedSegment] - -class DiarizedSegment(BaseModel): - """Segment with speaker information.""" - speaker: str # Speaker identifier - start: float - end: float - text: str - -# Translation types -class Translation(BaseModel): - text: str - -class TranslationVerbose(BaseModel): - text: str - language: str - duration: float - segments: list[TranscriptionSegment] | None - -# Model types -AudioModel = Literal["gpt-4o-transcribe", "gpt-4o-mini-transcribe", "gpt-4o-transcribe-diarize", "whisper-1"] -SpeechModel = Literal["tts-1", "tts-1-hd", "gpt-4o-mini-tts"] - -# File types -FileTypes = Union[ - FileContent, # File-like object - tuple[str | None, FileContent], # (filename, content) - tuple[str | None, FileContent, str | None] # (filename, content, content_type) -] - -# Response type for TTS -class HttpxBinaryResponseContent: - content: bytes - def read(self) -> bytes: ... - def iter_bytes(self, chunk_size: int = None) -> Iterator[bytes]: ... - def stream_to_file(self, file_path: str | Path) -> None: ... -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def transcribe_audio(): - client = AsyncOpenAI() - - with open("audio.mp3", "rb") as audio_file: - transcript = await client.audio.transcriptions.create( - model="whisper-1", - file=audio_file - ) - return transcript.text - -async def generate_speech(): - client = AsyncOpenAI() - - response = await client.audio.speech.create( - model="tts-1", - voice="alloy", - input="Async text to speech" - ) - response.stream_to_file("async_output.mp3") - -# Run async operations -text = asyncio.run(transcribe_audio()) -asyncio.run(generate_speech()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/batches.md b/.tessl/tiles/tessl/pypi-openai/docs/batches.md deleted file mode 100644 index bf7c3c163..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/batches.md +++ /dev/null @@ -1,526 +0,0 @@ -# Batch Processing - -Submit batch requests for asynchronous processing of multiple API calls. Process large volumes of requests cost-effectively with 50% discount on costs and 24-hour completion window. - -## Capabilities - -### Create Batch - -Create a batch request for async processing. - -```python { .api } -def create( - self, - *, - completion_window: str, - endpoint: str, - input_file_id: str, - metadata: dict[str, str] | Omit = omit, - output_expires_after: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Batch: - """ - Create a batch request for asynchronous processing. - - Args: - completion_window: Time frame for completion. Currently only "24h". - - endpoint: API endpoint for batch requests. Supported: - - "/v1/responses": Response API (structured outputs) - - "/v1/chat/completions": Chat completions - - "/v1/embeddings": Embeddings (max 50,000 inputs per batch) - - "/v1/completions": Text completions - - "/v1/moderations": Moderations - - input_file_id: ID of uploaded JSONL file with requests. - Each line: {"custom_id": "...", "method": "POST", "url": "...", "body": {...}} - - metadata: Optional key-value pairs (max 16). Keys max 64 chars, values max 512 chars. - - output_expires_after: Expiration policy for output and error files. - Dict with keys: - - anchor: "created_at" (file creation time) - - seconds: int (3600-2592000, i.e., 1 hour to 30 days) - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Batch: Created batch with status "validating". - - Raises: - BadRequestError: Invalid file format or endpoint - NotFoundError: Input file not found - """ -``` - -Usage examples: - -```python -from openai import OpenAI -import json - -client = OpenAI() - -# Create batch input file (JSONL format) -batch_requests = [ - { - "custom_id": "request-1", - "method": "POST", - "url": "/v1/chat/completions", - "body": { - "model": "gpt-3.5-turbo", - "messages": [{"role": "user", "content": "What is 2+2?"}] - } - }, - { - "custom_id": "request-2", - "method": "POST", - "url": "/v1/chat/completions", - "body": { - "model": "gpt-3.5-turbo", - "messages": [{"role": "user", "content": "What is the capital of France?"}] - } - } -] - -# Write to JSONL file -with open("batch_requests.jsonl", "w") as f: - for request in batch_requests: - f.write(json.dumps(request) + "\n") - -# Upload file -with open("batch_requests.jsonl", "rb") as f: - batch_file = client.files.create(file=f, purpose="batch") - -# Create batch -batch = client.batches.create( - input_file_id=batch_file.id, - endpoint="/v1/chat/completions", - completion_window="24h" -) - -print(f"Batch ID: {batch.id}") -print(f"Status: {batch.status}") - -# With metadata -batch = client.batches.create( - input_file_id=batch_file.id, - endpoint="/v1/chat/completions", - completion_window="24h", - metadata={ - "experiment": "test-run-1", - "dataset": "evaluation-set" - } -) - -# With output file expiration (e.g., 7 days) -batch = client.batches.create( - input_file_id=batch_file.id, - endpoint="/v1/chat/completions", - completion_window="24h", - output_expires_after={ - "anchor": "created_at", - "seconds": 604800 # 7 days - } -) - -# Embeddings batch -embeddings_requests = [ - { - "custom_id": "embed-1", - "method": "POST", - "url": "/v1/embeddings", - "body": { - "model": "text-embedding-3-small", - "input": "Sample text 1" - } - }, - { - "custom_id": "embed-2", - "method": "POST", - "url": "/v1/embeddings", - "body": { - "model": "text-embedding-3-small", - "input": "Sample text 2" - } - } -] - -with open("embed_requests.jsonl", "w") as f: - for request in embeddings_requests: - f.write(json.dumps(request) + "\n") - -with open("embed_requests.jsonl", "rb") as f: - embed_file = client.files.create(file=f, purpose="batch") - -batch = client.batches.create( - input_file_id=embed_file.id, - endpoint="/v1/embeddings", - completion_window="24h" -) -``` - -### Retrieve Batch - -Get batch status and results. - -```python { .api } -def retrieve( - self, - batch_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Batch: - """ - Retrieve batch details and status. - - Args: - batch_id: The ID of the batch. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Batch: Batch details with current status. - - Raises: - NotFoundError: Batch not found - """ -``` - -Usage example: - -```python -# Check batch status -batch = client.batches.retrieve("batch_abc123") - -print(f"Status: {batch.status}") -print(f"Total: {batch.request_counts.total}") -print(f"Completed: {batch.request_counts.completed}") -print(f"Failed: {batch.request_counts.failed}") - -if batch.status == "completed": - # Download results - if batch.output_file_id: - result_content = client.files.content(batch.output_file_id) - - # Parse JSONL results - import json - results = [] - for line in result_content.text.split("\n"): - if line.strip(): - results.append(json.loads(line)) - - for result in results: - custom_id = result["custom_id"] - response = result["response"] - print(f"{custom_id}: {response}") - - # Check error file if any failed - if batch.error_file_id: - error_content = client.files.content(batch.error_file_id) - print("Errors:", error_content.text) -``` - -### List Batches - -List batch requests with pagination. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Batch]: - """ - List batches with pagination. - - Args: - after: Cursor for pagination. Return batches after this batch ID. - limit: Number of batches to retrieve (max 100). Default 20. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[Batch]: Paginated list of batches. - """ -``` - -Usage example: - -```python -# List all batches -batches = client.batches.list() - -for batch in batches: - print(f"{batch.id}: {batch.status}") - -# Pagination -page1 = client.batches.list(limit=10) -page2 = client.batches.list(limit=10, after=page1.data[-1].id) - -# Filter by status -completed_batches = [ - b for b in client.batches.list() - if b.status == "completed" -] -``` - -### Cancel Batch - -Cancel an in-progress batch. - -```python { .api } -def cancel( - self, - batch_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Batch: - """ - Cancel a batch that is in progress. - - Args: - batch_id: The ID of the batch to cancel. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Batch: Batch with status "cancelling" or "cancelled". - - Raises: - NotFoundError: Batch not found - BadRequestError: Batch not in cancellable state - """ -``` - -Usage example: - -```python -# Cancel batch -batch = client.batches.cancel("batch_abc123") - -print(f"Status: {batch.status}") # "cancelling" or "cancelled" -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Batch(BaseModel): - """Batch request.""" - id: str - completion_window: str - created_at: int - endpoint: str - input_file_id: str - object: Literal["batch"] - status: Literal[ - "validating", "failed", "in_progress", - "finalizing", "completed", "expired", "cancelling", "cancelled" - ] - cancelled_at: int | None - cancelling_at: int | None - completed_at: int | None - error_file_id: str | None - errors: dict | None - expired_at: int | None - expires_at: int | None - failed_at: int | None - finalizing_at: int | None - in_progress_at: int | None - metadata: dict[str, str] | None - output_file_id: str | None - request_counts: BatchRequestCounts - -class BatchRequestCounts(BaseModel): - """Request count statistics.""" - completed: int - failed: int - total: int - -class OutputExpiresAfter(BaseModel): - """Expiration policy for batch output files.""" - anchor: Literal["created_at"] # File creation time anchor - seconds: int # Expiration time in seconds (3600-2592000) - -# Pagination -class SyncCursorPage[T](BaseModel): - data: list[T] - object: str - first_id: str | None - last_id: str | None - has_more: bool - def __iter__(self) -> Iterator[T]: ... -``` - -## Input File Format - -Batch input JSONL format: - -```jsonl -{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Hello!"}]}} -{"custom_id": "request-2", "method": "POST", "url": "/v1/embeddings", "body": {"model": "text-embedding-3-small", "input": "Hello world"}} -``` - -## Output File Format - -Batch output JSONL format: - -```jsonl -{"id": "batch_req_abc", "custom_id": "request-1", "response": {"status_code": 200, "request_id": "req_xyz", "body": {"id": "chatcmpl-123", "object": "chat.completion", ...}}, "error": null} -{"id": "batch_req_def", "custom_id": "request-2", "response": {"status_code": 200, "request_id": "req_uvw", "body": {"object": "list", "data": [...]}}, "error": null} -``` - -## Best Practices - -```python -from openai import OpenAI -import json -import time - -client = OpenAI() - -# 1. Monitor batch progress -def wait_for_batch(batch_id: str, poll_interval: int = 60): - """Wait for batch to complete.""" - while True: - batch = client.batches.retrieve(batch_id) - - if batch.status in ["completed", "failed", "cancelled", "expired"]: - return batch - - print(f"Status: {batch.status}") - print(f"Progress: {batch.request_counts.completed}/{batch.request_counts.total}") - time.sleep(poll_interval) - -# 2. Process results efficiently -def process_batch_results(batch_id: str): - """Download and process batch results.""" - batch = client.batches.retrieve(batch_id) - - if batch.status != "completed": - raise Exception(f"Batch not completed: {batch.status}") - - # Download results - result_content = client.files.content(batch.output_file_id) - - results = {} - for line in result_content.text.split("\n"): - if line.strip(): - result = json.loads(line) - custom_id = result["custom_id"] - response = result["response"]["body"] - results[custom_id] = response - - return results - -# 3. Handle errors -def get_batch_errors(batch_id: str): - """Get failed requests from batch.""" - batch = client.batches.retrieve(batch_id) - - if not batch.error_file_id: - return [] - - error_content = client.files.content(batch.error_file_id) - - errors = [] - for line in error_content.text.split("\n"): - if line.strip(): - errors.append(json.loads(line)) - - return errors - -# 4. Retry failed requests -def retry_failed_requests(batch_id: str): - """Create new batch with failed requests.""" - errors = get_batch_errors(batch_id) - - if not errors: - return None - - # Create retry input file - with open("retry_requests.jsonl", "w") as f: - for error in errors: - # Reconstruct request from error - request = { - "custom_id": error["custom_id"], - "method": error["method"], - "url": error["url"], - "body": error["body"] - } - f.write(json.dumps(request) + "\n") - - # Upload and create new batch - with open("retry_requests.jsonl", "rb") as f: - retry_file = client.files.create(file=f, purpose="batch") - - return client.batches.create( - input_file_id=retry_file.id, - endpoint=errors[0]["url"], - completion_window="24h", - metadata={"retry_of": batch_id} - ) - -# Complete workflow -# 1. Create batch -batch = create_batch() - -# 2. Wait for completion -completed_batch = wait_for_batch(batch.id) - -# 3. Process results -if completed_batch.status == "completed": - results = process_batch_results(batch.id) - print(f"Processed {len(results)} results") - - # Check for failures - if completed_batch.request_counts.failed > 0: - retry_batch = retry_failed_requests(batch.id) - print(f"Created retry batch: {retry_batch.id}") -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_batch(): - client = AsyncOpenAI() - - batch = await client.batches.create( - input_file_id="file-abc123", - endpoint="/v1/chat/completions", - completion_window="24h" - ) - - return batch.id - -batch_id = asyncio.run(create_batch()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/chat-completions.md b/.tessl/tiles/tessl/pypi-openai/docs/chat-completions.md deleted file mode 100644 index e1b9fad11..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/chat-completions.md +++ /dev/null @@ -1,1018 +0,0 @@ -# Chat Completions - -Create conversational responses using OpenAI's language models with support for text, vision inputs, audio, function calling, structured outputs, and streaming. Chat completions are the primary interface for interacting with models like GPT-4 and GPT-3.5. - -## Access Patterns - -Chat completions are accessible via: -- `client.chat.completions` - Primary access pattern -- `client.beta.chat.completions` - Beta namespace alias (identical to `client.chat`) - -## Capabilities - -### Create Chat Completion - -Generate a response for a chat conversation with extensive configuration options. - -```python { .api } -def create( - self, - *, - messages: Iterable[ChatCompletionMessageParam], - model: str | ChatModel, - audio: ChatCompletionAudioParam | None | Omit = omit, - frequency_penalty: float | None | Omit = omit, - function_call: dict | str | Omit = omit, - functions: Iterable[dict] | Omit = omit, - logit_bias: dict[str, int] | None | Omit = omit, - logprobs: bool | None | Omit = omit, - max_completion_tokens: int | None | Omit = omit, - max_tokens: int | None | Omit = omit, - metadata: dict[str, str] | None | Omit = omit, - modalities: list[Literal["text", "audio"]] | None | Omit = omit, - n: int | None | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - prediction: dict | None | Omit = omit, - presence_penalty: float | None | Omit = omit, - prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Literal["in-memory", "24h"] | None | Omit = omit, - reasoning_effort: Literal["none", "minimal", "low", "medium", "high"] | None | Omit = omit, - response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, - seed: int | None | Omit = omit, - service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None | Omit = omit, - stop: str | list[str] | None | Omit = omit, - store: bool | None | Omit = omit, - stream: bool | Omit = omit, - stream_options: dict | None | Omit = omit, - temperature: float | None | Omit = omit, - tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, - tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, - top_logprobs: int | None | Omit = omit, - top_p: float | None | Omit = omit, - user: str | Omit = omit, - verbosity: Literal["low", "medium", "high"] | None | Omit = omit, - web_search_options: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatCompletion | Stream[ChatCompletionChunk]: - """ - Create a model response for the given chat conversation. - - Args: - messages: List of messages comprising the conversation. Each message can be: - - System message: {"role": "system", "content": "..."} - - User message: {"role": "user", "content": "..."} - - Assistant message: {"role": "assistant", "content": "..."} - - Tool message: {"role": "tool", "content": "...", "tool_call_id": "..."} - Supports text, images, and audio content. - - model: Model ID like "gpt-4", "gpt-4-turbo", "gpt-3.5-turbo", or "o1". - See https://platform.openai.com/docs/models for available models. - - audio: Parameters for audio output when modalities includes "audio". - {"voice": "alloy|echo|fable|onyx|nova|shimmer", "format": "wav|mp3|flac|opus|pcm16"} - - frequency_penalty: Number between -2.0 and 2.0. Penalizes tokens based on frequency - in the text, reducing repetition. Default 0. - - function_call: (Deprecated, use tool_choice) Controls function calling. - - "none": No function calls - - "auto": Model decides - - {"name": "function_name"}: Force specific function - - functions: (Deprecated, use tools) List of function definitions. - - logit_bias: Modify token probabilities. Maps token IDs to bias values from - -100 to 100. Values near ±1 slightly adjust probability, ±100 ban/force tokens. - - logprobs: If true, returns log probabilities of output tokens. - - max_completion_tokens: Maximum tokens for completion including reasoning tokens. - Preferred over max_tokens for o-series models. - - max_tokens: (Deprecated for o-series) Maximum tokens in completion. - Use max_completion_tokens instead. - - metadata: Up to 16 key-value pairs for storing additional object information. - Keys max 64 chars, values max 512 chars. - - modalities: Output types to generate. Options: ["text"], ["audio"], ["text", "audio"]. - Default ["text"]. Audio requires gpt-4o-audio-preview model. - - n: Number of completion choices to generate. Default 1. - Costs scale with number of choices. - - parallel_tool_calls: Enable parallel function calling during tool use. - Default true when tools are present. - - prediction: Static predicted output for regeneration tasks (e.g., file content). - - presence_penalty: Number between -2.0 and 2.0. Penalizes tokens based on - presence in text, encouraging new topics. Default 0. - - prompt_cache_key: Cache identifier for optimizing similar requests. - Replaces the user field for caching. - - prompt_cache_retention: Cache retention policy. "24h" enables extended caching - up to 24 hours. Default "in-memory". - - reasoning_effort: Effort level for reasoning models (o-series). - Options: "none", "minimal", "low", "medium", "high". - - gpt-5.1: defaults to "none" - - Other models: default "medium" - - gpt-5-pro: only supports "high" - - response_format: Output format specification. - - {"type": "text"}: Plain text (default) - - {"type": "json_object"}: Valid JSON - - {"type": "json_schema", "json_schema": {...}}: Structured Outputs - - safety_identifier: Stable user identifier (hashed) for policy violation detection. - - seed: For deterministic sampling (Beta). Same seed + parameters should return - same result, but not guaranteed. Check system_fingerprint for changes. - - service_tier: Processing type for serving. Options: "auto", "default", "flex", - "scale", "priority". Affects latency and pricing. - - stop: Up to 4 sequences where generation stops. Can be string or list of strings. - - store: If true, stores the completion for model distillation/evals. - - stream: If true, returns SSE stream of ChatCompletionChunk objects. - Returns Stream[ChatCompletionChunk] instead of ChatCompletion. - - stream_options: Streaming configuration. Accepts dict with: - - "include_usage": bool - If true, includes token usage in final chunk - - "include_obfuscation": bool - If true (default), adds random characters - to obfuscation field on streaming delta events to normalize payload sizes - as mitigation for side-channel attacks. Set to false to optimize bandwidth. - - temperature: Sampling temperature between 0 and 2. Higher values (e.g., 0.8) - make output more random, lower values (e.g., 0.2) more deterministic. - Default 1. Alter this or top_p, not both. - - tool_choice: Controls tool/function calling. - - "none": No tools called - - "auto": Model decides (default when tools present) - - "required": Model must call at least one tool - - {"type": "function", "function": {"name": "..."}}: Force specific tool - - tools: List of tools/functions available to the model. Each tool: - { - "type": "function", - "function": { - "name": "function_name", - "description": "What it does", - "parameters": {...} # JSON Schema - } - } - - top_logprobs: Number of most likely tokens to return with logprobs (0-20). - Requires logprobs=true. - - top_p: Nucleus sampling parameter between 0 and 1. Model considers tokens - with top_p probability mass. E.g., 0.1 means only tokens in top 10%. - Default 1. Alter this or temperature, not both. - - user: (Deprecated for caching, use prompt_cache_key) Unique user identifier - for abuse monitoring. - - verbosity: Output detail level for reasoning models. Options: "low", "medium", "high". - - web_search_options: Web search configuration (if available for model). - - extra_headers: Additional HTTP headers for the request. - - extra_query: Additional query parameters for the request. - - extra_body: Additional JSON fields for the request body. - - timeout: Request timeout in seconds. - - Returns: - ChatCompletion: If stream=False (default), returns complete response. - Stream[ChatCompletionChunk]: If stream=True, returns streaming response. - - Raises: - BadRequestError: Invalid parameters - AuthenticationError: Invalid API key - RateLimitError: Rate limit exceeded - APIError: Other API errors - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic chat completion -response = client.chat.completions.create( - model="gpt-4", - messages=[ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "What is the capital of France?"} - ] -) -print(response.choices[0].message.content) - -# With multiple messages and temperature -response = client.chat.completions.create( - model="gpt-3.5-turbo", - messages=[ - {"role": "system", "content": "You are a creative writer."}, - {"role": "user", "content": "Write a haiku about coding."}, - ], - temperature=0.8, - max_tokens=100 -) - -# With vision (image input) -response = client.chat.completions.create( - model="gpt-4-turbo", - messages=[ - { - "role": "user", - "content": [ - {"type": "text", "text": "What's in this image?"}, - { - "type": "image_url", - "image_url": {"url": "https://example.com/image.jpg"} - } - ] - } - ] -) - -# With function calling / tools -tools = [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get current weather for a location", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "City name, e.g., San Francisco" - }, - "unit": { - "type": "string", - "enum": ["celsius", "fahrenheit"] - } - }, - "required": ["location"] - } - } - } -] - -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "What's the weather in Boston?"}], - tools=tools, - tool_choice="auto" -) - -# Check if model wants to call a function -if response.choices[0].message.tool_calls: - for tool_call in response.choices[0].message.tool_calls: - print(f"Function: {tool_call.function.name}") - print(f"Arguments: {tool_call.function.arguments}") - -# Streaming response -stream = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Tell me a story."}], - stream=True -) - -for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") - -# With reasoning effort (o-series models) -response = client.chat.completions.create( - model="o1", - messages=[ - {"role": "user", "content": "Solve this complex math problem: ..."} - ], - reasoning_effort="high" -) - -# With structured output (JSON Schema) -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "List 3 colors"}], - response_format={ - "type": "json_schema", - "json_schema": { - "name": "colors_response", - "strict": True, - "schema": { - "type": "object", - "properties": { - "colors": { - "type": "array", - "items": {"type": "string"} - } - }, - "required": ["colors"], - "additionalProperties": False - } - } - } -) -``` - -### Parse with Structured Output - -Create chat completion with automatic Pydantic model parsing for structured outputs. - -```python { .api } -def parse( - self, - *, - messages: Iterable[ChatCompletionMessageParam], - model: str | ChatModel, - response_format: type[ResponseFormatT] | Omit = omit, - audio: ChatCompletionAudioParam | None | Omit = omit, - frequency_penalty: float | None | Omit = omit, - function_call: dict | str | Omit = omit, - functions: Iterable[dict] | Omit = omit, - logit_bias: dict[str, int] | None | Omit = omit, - logprobs: bool | None | Omit = omit, - max_completion_tokens: int | None | Omit = omit, - max_tokens: int | None | Omit = omit, - metadata: dict[str, str] | None | Omit = omit, - modalities: list[Literal["text", "audio"]] | None | Omit = omit, - n: int | None | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - prediction: dict | None | Omit = omit, - presence_penalty: float | None | Omit = omit, - prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Literal["in-memory", "24h"] | None | Omit = omit, - reasoning_effort: Literal["none", "minimal", "low", "medium", "high"] | None | Omit = omit, - safety_identifier: str | Omit = omit, - seed: int | None | Omit = omit, - service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None | Omit = omit, - stop: str | list[str] | None | Omit = omit, - store: bool | None | Omit = omit, - stream_options: dict | None | Omit = omit, - temperature: float | None | Omit = omit, - tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, - tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, - top_logprobs: int | None | Omit = omit, - top_p: float | None | Omit = omit, - user: str | Omit = omit, - verbosity: Literal["low", "medium", "high"] | None | Omit = omit, - web_search_options: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ParsedChatCompletion[ResponseFormatT]: - """ - Create chat completion with automatic Pydantic model parsing. - - Converts Pydantic model to JSON schema, sends to API, and parses response - back into the model. Also automatically parses function tool calls when - using pydantic_function_tool() or strict mode. - - Args: - messages: List of conversation messages. - model: Model ID to use. - response_format: Pydantic model class for structured output. - (other parameters same as create method) - - Returns: - ParsedChatCompletion[ResponseFormatT]: Completion with parsed content. - Access via completion.choices[0].message.parsed - - Raises: - Same as create method, plus validation errors for malformed responses. - """ -``` - -Usage example: - -```python -from pydantic import BaseModel -from openai import OpenAI - -# Define response structure -class Step(BaseModel): - explanation: str - output: str - -class MathResponse(BaseModel): - steps: list[Step] - final_answer: str - -client = OpenAI() - -# Parse returns strongly-typed response -completion = client.chat.completions.parse( - model="gpt-4", - messages=[ - {"role": "system", "content": "You are a helpful math tutor."}, - {"role": "user", "content": "Solve: 8x + 31 = 2"} - ], - response_format=MathResponse -) - -# Access parsed content with full type safety -message = completion.choices[0].message -if message.parsed: - for step in message.parsed.steps: - print(f"{step.explanation}: {step.output}") - print(f"Answer: {message.parsed.final_answer}") - -# With function tools using pydantic -from openai import pydantic_function_tool - -class WeatherParams(BaseModel): - location: str - unit: Literal["celsius", "fahrenheit"] = "celsius" - -tool = pydantic_function_tool( - WeatherParams, - name="get_weather", - description="Get current weather" -) - -completion = client.chat.completions.parse( - model="gpt-4", - messages=[{"role": "user", "content": "What's the weather in NYC?"}], - tools=[tool], - response_format=MathResponse # For assistant's final response -) - -# Tool calls are automatically parsed -if completion.choices[0].message.tool_calls: - for call in completion.choices[0].message.tool_calls: - if call.type == "function": - # call.function.parsed_arguments is a WeatherParams instance - params = call.function.parsed_arguments - print(f"Location: {params.location}, Unit: {params.unit}") -``` - -### Stored Chat Completions - -Store, retrieve, update, and delete chat completions for persistent conversation management. - -```python { .api } -def retrieve( - self, - completion_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatCompletion: - """ - Retrieve a previously stored chat completion by its ID. - - Args: - completion_id: The ID of the stored chat completion to retrieve. - - Returns: - ChatCompletion: The stored completion object. - """ - -def update( - self, - completion_id: str, - *, - metadata: dict[str, str] | None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatCompletion: - """ - Update metadata for a stored chat completion. - - Args: - completion_id: The ID of the stored completion to update. - metadata: Updated metadata key-value pairs (max 16 pairs). Required parameter. - - Returns: - ChatCompletion: The updated completion object. - """ - -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - metadata: dict[str, str] | None | Omit = omit, - model: str | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[ChatCompletion]: - """ - List stored chat completions with cursor-based pagination. - - Args: - after: Cursor for pagination (ID of object to start after). - limit: Maximum number of completions to return (default 20, max 100). - metadata: Filter by metadata key-value pairs. Only returns completions with matching metadata. - model: Filter by model. Only returns completions generated with the specified model. - order: Sort order: "asc" (oldest first) or "desc" (newest first). - - Returns: - SyncCursorPage[ChatCompletion]: Paginated list of completions. - """ - -def delete( - self, - completion_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatCompletionDeleted: - """ - Delete a stored chat completion. - - Args: - completion_id: The ID of the stored completion to delete. - - Returns: - ChatCompletionDeleted: Confirmation of deletion with deleted=True. - """ -``` - -Access stored completion messages: - -```python { .api } -# Via client.chat.completions.messages.list() -def list( - self, - completion_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[ChatCompletionStoreMessage]: - """ - List messages from a stored chat completion. - - Args: - completion_id: The ID of the stored completion. - after: Cursor for pagination. - limit: Maximum number of messages to return. - order: Sort order: "asc" or "desc". - - Returns: - SyncCursorPage[ChatCompletionStoreMessage]: Paginated list of messages. - """ -``` - -#### Usage Example - -```python -from openai import OpenAI - -client = OpenAI() - -# Create a chat completion with store=True -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Tell me about Python"}], - store=True, - metadata={"user_id": "user123", "session": "abc"} -) - -completion_id = response.id -print(f"Stored completion: {completion_id}") - -# Retrieve the stored completion later -stored = client.chat.completions.retrieve(completion_id) -print(stored.choices[0].message.content) - -# Update metadata -updated = client.chat.completions.update( - completion_id, - metadata={"user_id": "user123", "session": "abc", "reviewed": "true"} -) - -# List all stored completions -page = client.chat.completions.list(limit=10, order="desc") -for completion in page.data: - print(f"{completion.id}: {completion.created}") - -# List messages from a specific completion -messages_page = client.chat.completions.messages.list(completion_id) -for message in messages_page.data: - print(f"{message.role}: {message.content}") - -# Delete when no longer needed -deleted = client.chat.completions.delete(completion_id) -print(f"Deleted: {deleted.deleted}") -``` - -### Stream Chat Completions - -Wrapper over `create(stream=True)` that provides a more granular event API and automatic accumulation of deltas. Requires use within a context manager. - -```python { .api } -def stream( - self, - *, - messages: Iterable[ChatCompletionMessageParam], - model: str | ChatModel, - audio: ChatCompletionAudioParam | None | Omit = omit, - frequency_penalty: float | None | Omit = omit, - function_call: dict | str | Omit = omit, - functions: Iterable[dict] | Omit = omit, - logit_bias: dict[str, int] | None | Omit = omit, - logprobs: bool | None | Omit = omit, - max_completion_tokens: int | None | Omit = omit, - max_tokens: int | None | Omit = omit, - metadata: dict[str, str] | None | Omit = omit, - modalities: list[Literal["text", "audio"]] | None | Omit = omit, - n: int | None | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - prediction: dict | None | Omit = omit, - presence_penalty: float | None | Omit = omit, - prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Literal["in-memory", "24h"] | None | Omit = omit, - reasoning_effort: Literal["none", "minimal", "low", "medium", "high"] | None | Omit = omit, - response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, - seed: int | None | Omit = omit, - service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None | Omit = omit, - stop: str | list[str] | None | Omit = omit, - store: bool | None | Omit = omit, - stream_options: dict | None | Omit = omit, - temperature: float | None | Omit = omit, - tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, - tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, - top_logprobs: int | None | Omit = omit, - top_p: float | None | Omit = omit, - user: str | Omit = omit, - verbosity: Literal["low", "medium", "high"] | None | Omit = omit, - web_search_options: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatCompletionStreamManager: - """ - Streaming wrapper with granular event API and automatic delta accumulation. - - Unlike create(stream=True), this method requires a context manager to prevent - resource leaks. Yields detailed events including content.delta, content.done, - and provides accumulated snapshots. - - Args: - Same parameters as create() method. - - Returns: - ChatCompletionStreamManager: Context manager yielding stream events. - """ -``` - -**Usage Example:** - -```python -from openai import OpenAI - -client = OpenAI() - -# Must use within context manager -with client.chat.completions.stream( - model="gpt-4", - messages=[{"role": "user", "content": "Tell me a story"}], -) as stream: - for event in stream: - if event.type == "content.delta": - print(event.delta, flush=True, end="") - elif event.type == "content.done": - print(f"\nFinal content: {event.content}") - -# Access accumulated result after streaming -print(f"Model: {stream.response.model}") -print(f"Total tokens: {stream.response.usage.total_tokens}") -``` - -### Streaming with Helpers - -Advanced streaming with context managers for easier handling. - -```python -from openai import OpenAI - -client = OpenAI() - -# Using stream() method with context manager -with client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Count to 5"}], - stream=True -) as stream: - for chunk in stream: - print(chunk.choices[0].delta.content or "", end="") - -# Using stream context manager explicitly -stream = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Tell me a joke"}], - stream=True -) - -# Access streaming response -for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="", flush=True) - -# Stream with usage information -stream = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}], - stream=True, - stream_options={"include_usage": True} -) - -for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") - # Usage included in final chunk - if hasattr(chunk, 'usage') and chunk.usage: - print(f"\nTokens used: {chunk.usage.total_tokens}") -``` - -## Types - -```python { .api } -from typing import Literal, TypeVar, Generic -from pydantic import BaseModel -from openai.types.chat import ( - ChatCompletionToolUnionParam, - ChatCompletionToolChoiceOptionParam, - completion_create_params, -) - -# Message types -ChatCompletionMessageParam = dict[str, Any] # Union of message types - -class ChatCompletionSystemMessageParam(TypedDict): - role: Literal["system"] - content: str - name: NotRequired[str] - -class ChatCompletionUserMessageParam(TypedDict): - role: Literal["user"] - content: str | list[dict] # Text or multimodal content - name: NotRequired[str] - -class ChatCompletionAssistantMessageParam(TypedDict): - role: Literal["assistant"] - content: str | None - name: NotRequired[str] - tool_calls: NotRequired[list[dict]] - -class ChatCompletionToolMessageParam(TypedDict): - role: Literal["tool"] - content: str - tool_call_id: str - -# Response types -class ChatCompletion(BaseModel): - id: str - choices: list[Choice] - created: int - model: str - object: Literal["chat.completion"] - system_fingerprint: str | None - usage: CompletionUsage | None - -class Choice(BaseModel): - finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"] - index: int - logprobs: Logprobs | None - message: ChatCompletionMessage - -class ChatCompletionMessage(BaseModel): - content: str | None - role: Literal["assistant"] - tool_calls: list[ChatCompletionMessageToolCall] | None - function_call: FunctionCall | None # Deprecated - audio: Audio | None # When modalities includes audio - -class ChatCompletionStoreMessage(BaseModel): - """Message from a stored chat completion.""" - content: str | None - role: Literal["system", "user", "assistant", "tool"] - tool_calls: list[ChatCompletionMessageToolCall] | None - tool_call_id: str | None # For tool messages - -class ChatCompletionMessageToolCall(BaseModel): - id: str - function: Function - type: Literal["function"] - -class Function(BaseModel): - arguments: str # JSON string - name: str - -class CompletionUsage(BaseModel): - completion_tokens: int - prompt_tokens: int - total_tokens: int - completion_tokens_details: CompletionTokensDetails | None - -# Streaming types -class ChatCompletionChunk(BaseModel): - id: str - choices: list[ChunkChoice] - created: int - model: str - object: Literal["chat.completion.chunk"] - system_fingerprint: str | None - usage: CompletionUsage | None # Only in final chunk with include_usage - -class ChunkChoice(BaseModel): - delta: ChoiceDelta - finish_reason: str | None - index: int - logprobs: Logprobs | None - -class ChoiceDelta(BaseModel): - content: str | None - role: Literal["assistant"] | None - tool_calls: list[ChoiceDeltaToolCall] | None - -# Parsed completion types -ResponseFormatT = TypeVar("ResponseFormatT", bound=BaseModel) - -class ParsedChatCompletion(Generic[ResponseFormatT], ChatCompletion): - """ChatCompletion with parsed content.""" - choices: list[ParsedChoice[ResponseFormatT]] - -class ParsedChoice(Generic[ResponseFormatT], Choice): - message: ParsedChatCompletionMessage[ResponseFormatT] - -class ParsedChatCompletionMessage(Generic[ResponseFormatT], ChatCompletionMessage): - parsed: ResponseFormatT | None - tool_calls: list[ParsedFunctionToolCall] | None - -class ParsedFunctionToolCall(ChatCompletionMessageToolCall): - function: ParsedFunction - type: Literal["function"] - -class ParsedFunction(Function): - parsed_arguments: BaseModel | None - -# Deletion type -class ChatCompletionDeleted(BaseModel): - id: str - deleted: bool - object: Literal["chat.completion"] - -# Tool/function definitions -class ChatCompletionToolParam(TypedDict): - type: Literal["function"] - function: FunctionDefinition - -class FunctionDefinition(TypedDict): - name: str - description: NotRequired[str] - parameters: dict # JSON Schema - strict: NotRequired[bool] # Enable strict schema adherence - -# Response format types -class ResponseFormatText(TypedDict): - type: Literal["text"] - -class ResponseFormatJSONObject(TypedDict): - type: Literal["json_object"] - -class ResponseFormatJSONSchema(TypedDict): - type: Literal["json_schema"] - json_schema: JSONSchema - -class JSONSchema(TypedDict): - name: str - description: NotRequired[str] - schema: dict # JSON Schema object - strict: NotRequired[bool] - -# Audio types -class ChatCompletionAudioParam(TypedDict): - voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"] - format: Literal["wav", "mp3", "flac", "opus", "pcm16"] - -# Streaming options -class ChatCompletionStreamOptionsParam(TypedDict): - include_usage: NotRequired[bool] - -# Tool choice types -ChatCompletionToolChoiceOptionParam = ( - Literal["none", "auto", "required"] | dict -) - -class ToolChoiceFunction(TypedDict): - type: Literal["function"] - function: FunctionChoice - -class FunctionChoice(TypedDict): - name: str - -# Stream wrapper type -class Stream(Generic[T]): - def __iter__(self) -> Iterator[T]: ... - def __next__(self) -> T: ... - def __enter__(self) -> Stream[T]: ... - def __exit__(self, *args) -> None: ... - def close(self) -> None: ... -``` - -## Async Usage - -All chat completion methods are available in async variants through `AsyncOpenAI`: - -```python -import asyncio -from openai import AsyncOpenAI - -async def main(): - client = AsyncOpenAI() - - # Async create - returns ChatCompletion or AsyncStream[ChatCompletionChunk] - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) - print(response.choices[0].message.content) - - # Async streaming - async for chunk in await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Tell me a story"}], - stream=True - ): - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") - - # Async parse - returns ParsedChatCompletion with structured output - from pydantic import BaseModel - - class CalendarEvent(BaseModel): - name: str - date: str - participants: list[str] - - response = await client.chat.completions.parse( - model="gpt-4o-2024-08-06", - messages=[{"role": "user", "content": "Alice and Bob are meeting on Friday"}], - response_format=CalendarEvent - ) - event = response.choices[0].message.parsed - - # Other async methods: retrieve, update, list, delete, stream - # All have the same signatures as sync versions - -asyncio.run(main()) -``` - -**Note**: AsyncOpenAI uses `AsyncStream[ChatCompletionChunk]` for streaming responses instead of `Stream[ChatCompletionChunk]`. - -## Error Handling - -```python -from openai import OpenAI, APIError, RateLimitError - -client = OpenAI() - -try: - response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello"}] - ) -except RateLimitError as e: - print(f"Rate limit hit: {e}") - # Handle rate limiting (e.g., retry with backoff) -except APIError as e: - print(f"API error: {e.status_code} - {e.message}") - # Handle other API errors -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/chatkit.md b/.tessl/tiles/tessl/pypi-openai/docs/chatkit.md deleted file mode 100644 index 2eec50acb..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/chatkit.md +++ /dev/null @@ -1,404 +0,0 @@ -# ChatKit (Beta) - -ChatKit provides a simplified, high-level interface for building chat applications with OpenAI models. It abstracts away common patterns and provides session and thread management out of the box. - -**Note**: ChatKit is a beta feature and the API may change. - -## Capabilities - -### Create ChatKit Session - -Create a new ChatKit session for managing a chat conversation with a specified workflow. - -```python { .api } -def create( - self, - *, - user: str, - workflow: ChatSessionWorkflowParam, - chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, - expires_after: ChatSessionExpiresAfterParam | Omit = omit, - rate_limits: ChatSessionRateLimitsParam | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatSession: - """ - Create a ChatKit session with a workflow and configuration. - - Args: - user: A free-form string that identifies your end user; ensures this Session can access other objects that have the same `user` scope. - workflow: Workflow that powers the session. Must include an `id` field (workflow identifier) and optionally `state_variables`, `tracing`, and `version`. - chatkit_configuration: Optional overrides for ChatKit runtime configuration features including automatic thread titling, file upload settings, and history management. - expires_after: Optional override for session expiration timing in seconds from creation. Defaults to 10 minutes (600 seconds). - rate_limits: Optional override for per-minute request limits. When omitted, defaults to 10 requests per minute. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ChatSession: Created session with configuration, client secret, and workflow metadata. - """ -``` - -### Cancel ChatKit Session - -Cancel an active ChatKit session and release resources. - -```python { .api } -def cancel( - self, - session_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatSession: - """ - Cancel a ChatKit session. - - Args: - session_id: The ID of the session to cancel. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ChatSession: Session with status "cancelled". - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create a ChatKit session with a workflow -session = client.beta.chatkit.sessions.create( - user="user_12345", - workflow={ - "id": "workflow_abc123", - "state_variables": {"context": "customer_support"}, - "version": "v1.0" - } -) - -print(f"Session ID: {session.id}") -print(f"Status: {session.status}") -print(f"User: {session.user}") -print(f"Expires at: {session.expires_at}") - -# Cancel session when done -cancelled = client.beta.chatkit.sessions.cancel(session.id) -print(f"Cancelled: {cancelled.status}") -``` - -### Retrieve ChatKit Thread - -Get details of a specific ChatKit thread. - -```python { .api } -def retrieve( - self, - thread_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatKitThread: - """ - Retrieve a ChatKit thread by ID. - - Args: - thread_id: The ID of the thread to retrieve. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ChatKitThread: Thread object with messages and metadata. - """ -``` - -### List ChatKit Threads - -List all ChatKit threads for the current organization. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncConversationCursorPage[ChatKitThread]: - """ - List ChatKit threads with pagination. - - Args: - after: Return threads after this thread ID (for forward pagination). - before: Return threads before this thread ID (for backward pagination). - limit: Maximum number of threads to return (default 20, max 100). - order: Sort order: "asc" (oldest first) or "desc" (newest first, default). - user: Filter threads that belong to this user identifier (defaults to null for all users). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncConversationCursorPage[ChatKitThread]: Paginated list of threads with cursor-based navigation. - """ -``` - -### Delete ChatKit Thread - -Delete a ChatKit thread and all its messages. - -```python { .api } -def delete( - self, - thread_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ThreadDeleteResponse: - """ - Delete a ChatKit thread. - - Args: - thread_id: The ID of the thread to delete. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ThreadDeleteResponse: Deletion confirmation with deleted=True. - """ -``` - -### List Thread Items - -List messages and events within a ChatKit thread. - -```python { .api } -def list_items( - self, - thread_id: str, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncPage[ChatKitThreadItem]: - """ - List items (messages and events) in a ChatKit thread. - - Args: - thread_id: The ID of the thread. - after: Return items after this item ID. - before: Return items before this item ID. - limit: Maximum number of items to return (default 20, max 100). - order: Sort order: "asc" (chronological) or "desc" (reverse chronological). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncPage[ChatKitThreadItem]: Paginated list of thread items. - """ -``` - -## Complete Usage Example - -```python -from openai import OpenAI - -client = OpenAI() - -# Create a session with a workflow -session = client.beta.chatkit.sessions.create( - user="user_12345", - workflow={ - "id": "workflow_xyz", - "state_variables": {"mode": "qa"} - }, - rate_limits={"per_minute": 20} -) - -print(f"Session created: {session.id}") -print(f"Client secret: {session.client_secret}") - -# List all threads -threads = client.beta.chatkit.threads.list(limit=10) -for thread in threads.data: - print(f"Thread: {thread.id}, Created: {thread.created_at}") - -# Retrieve a specific thread -if threads.data: - thread = client.beta.chatkit.threads.retrieve(threads.data[0].id) - print(f"Thread metadata: {thread.metadata}") - - # List items in the thread - items = client.beta.chatkit.threads.list_items(thread.id, order="asc") - for item in items.data: - print(f"Item: {item.type} - {item.content}") - - # Delete thread when no longer needed - deleted = client.beta.chatkit.threads.delete(thread.id) - print(f"Deleted: {deleted.deleted}") - -# Cancel session -client.beta.chatkit.sessions.cancel(session.id) -``` - -## Types - -```python { .api } -from typing import Literal, Dict, Union -from pydantic import BaseModel - -# Session related types -class ChatSessionWorkflowParam: - """Workflow parameter for ChatKit session.""" - id: str # Required: Identifier for the workflow - state_variables: Dict[str, Union[str, bool, float]] | None # State variables forwarded to workflow - tracing: dict | None # Optional tracing overrides (enabled by default) - version: str | None # Specific workflow version (defaults to latest) - -class ChatSessionChatKitConfigurationParam: - """Optional ChatKit runtime configuration.""" - automatic_thread_titling: dict | None # Configuration for automatic thread title generation - file_upload: dict | None # File upload settings - history: dict | None # History management settings - -class ChatSessionExpiresAfterParam: - """Session expiration configuration.""" - # Typically a dict with duration in seconds - -class ChatSessionRateLimitsParam: - """Rate limiting configuration.""" - per_minute: int | None # Requests per minute limit - -class ChatSession(BaseModel): - """ChatKit session object.""" - id: str - """Identifier for the ChatKit session.""" - - object: Literal["chatkit.session"] - """Type discriminator that is always `chatkit.session`.""" - - chatkit_configuration: dict - """Resolved ChatKit feature configuration for the session.""" - - client_secret: str - """Ephemeral client secret that authenticates session requests.""" - - expires_at: int - """Unix timestamp (in seconds) for when the session expires.""" - - max_requests_per_1_minute: int - """Convenience copy of the per-minute request limit.""" - - rate_limits: dict - """Resolved rate limit values.""" - - status: str - """Current lifecycle state of the session (e.g., "active", "cancelled").""" - - user: str - """User identifier associated with the session.""" - - workflow: dict - """Workflow metadata for the session.""" - -class ChatKitThread(BaseModel): - """ChatKit thread object.""" - id: str - object: Literal["chatkit.thread"] - created_at: int - metadata: dict[str, str] | None - session_id: str | None - -class ThreadDeleteResponse(BaseModel): - """Thread deletion confirmation.""" - id: str - object: Literal["chatkit.thread"] - deleted: bool - -class ChatKitThreadItem(BaseModel): - """Item within a ChatKit thread (message or event).""" - id: str - object: Literal["chatkit.thread.item"] - type: Literal["message", "event"] - content: str | dict - role: Literal["user", "assistant", "system"] | None - created_at: int - metadata: dict[str, str] | None - -# Pagination -class SyncPage[T](BaseModel): - data: list[T] - object: str - first_id: str | None - last_id: str | None - has_more: bool - def __iter__(self) -> Iterator[T]: ... -``` - -## Best Practices - -1. **Session Management**: Create one session per conversation context and cancel when done to free resources. - -2. **Thread Organization**: Use separate threads for distinct conversation topics or user contexts. - -3. **Metadata**: Leverage metadata for tracking user IDs, session types, or application-specific data. - -4. **Pagination**: When listing threads or items, use pagination parameters to efficiently handle large datasets. - -5. **Error Handling**: Always wrap ChatKit calls in try-except blocks to handle API errors gracefully. - -6. **Resource Cleanup**: Always cancel sessions and delete threads when they're no longer needed. - -```python -from openai import OpenAI, APIError - -client = OpenAI() - -try: - session = client.beta.chatkit.sessions.create( - user="user_abc", - workflow={"id": "workflow_123"} - ) - - # Use session... - -finally: - # Clean up - try: - client.beta.chatkit.sessions.cancel(session.id) - except APIError: - pass # Session may already be cancelled -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/client-initialization.md b/.tessl/tiles/tessl/pypi-openai/docs/client-initialization.md deleted file mode 100644 index 043e66a74..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/client-initialization.md +++ /dev/null @@ -1,740 +0,0 @@ -# Client Initialization - -Initialize OpenAI and Azure OpenAI clients with API credentials, configuration options, and custom HTTP settings. The library provides both synchronous and asynchronous implementations for all clients. - -## Capabilities - -### OpenAI Client - -Creates a synchronous client for making API calls to OpenAI's services. - -```python { .api } -class OpenAI: - """ - Synchronous client for OpenAI API. - - Automatically infers credentials from environment variables: - - api_key from OPENAI_API_KEY - - organization from OPENAI_ORG_ID - - project from OPENAI_PROJECT_ID - - webhook_secret from OPENAI_WEBHOOK_SECRET - - base_url from OPENAI_BASE_URL - """ - - def __init__( - self, - *, - api_key: str | Callable[[], str] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | httpx.URL | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: - """ - Construct a new synchronous OpenAI client instance. - - Args: - api_key: OpenAI API key. Can be a string or callable that returns a string. - If None, reads from OPENAI_API_KEY environment variable. - organization: OpenAI organization ID for multi-org accounts. - If None, reads from OPENAI_ORG_ID environment variable. - project: OpenAI project ID for project-scoped usage tracking. - If None, reads from OPENAI_PROJECT_ID environment variable. - webhook_secret: Secret for verifying webhook signatures. - If None, reads from OPENAI_WEBHOOK_SECRET environment variable. - base_url: Override the default API base URL (https://api.openai.com/v1). - If None, reads from OPENAI_BASE_URL environment variable. - websocket_base_url: Base URL for WebSocket connections (realtime API). - If None, defaults to base_url with wss:// scheme. - timeout: Request timeout in seconds. Can be a float or Timeout object. - Default is NOT_GIVEN which uses library defaults. - max_retries: Maximum number of retry attempts for failed requests. - Default is 2. - default_headers: Additional HTTP headers to include in all requests. - default_query: Additional query parameters to include in all requests. - http_client: Custom httpx.Client instance for advanced HTTP configuration. - _strict_response_validation: Enable strict API response validation. - Raises APIResponseValidationError if API returns invalid data. - - Raises: - OpenAIError: If api_key is not provided and OPENAI_API_KEY is not set. - """ -``` - -Usage example: - -```python -from openai import OpenAI - -# Basic initialization with API key -client = OpenAI(api_key="sk-...") - -# Using environment variable (OPENAI_API_KEY) -client = OpenAI() - -# Advanced configuration -client = OpenAI( - api_key="sk-...", - organization="org-...", - project="proj_...", - timeout=30.0, - max_retries=3, - default_headers={"X-Custom-Header": "value"} -) - -# Dynamic API key with callable -def get_api_key() -> str: - return retrieve_key_from_secure_storage() - -client = OpenAI(api_key=get_api_key) - -# Custom HTTP client with specific configuration -import httpx - -http_client = httpx.Client( - limits=httpx.Limits(max_keepalive_connections=10), - proxies="http://proxy.example.com:8080" -) -client = OpenAI( - api_key="sk-...", - http_client=http_client -) -``` - -### Async OpenAI Client - -Creates an asynchronous client for making non-blocking API calls to OpenAI's services. - -```python { .api } -class AsyncOpenAI: - """ - Asynchronous client for OpenAI API. - - Automatically infers credentials from environment variables: - - api_key from OPENAI_API_KEY - - organization from OPENAI_ORG_ID - - project from OPENAI_PROJECT_ID - - webhook_secret from OPENAI_WEBHOOK_SECRET - - base_url from OPENAI_BASE_URL - """ - - def __init__( - self, - *, - api_key: str | Callable[[], Awaitable[str]] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | httpx.URL | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: - """ - Construct a new asynchronous OpenAI client instance. - - Args: - api_key: OpenAI API key. Can be a string, sync callable, or async callable. - If None, reads from OPENAI_API_KEY environment variable. - organization: OpenAI organization ID for multi-org accounts. - If None, reads from OPENAI_ORG_ID environment variable. - project: OpenAI project ID for project-scoped usage tracking. - If None, reads from OPENAI_PROJECT_ID environment variable. - webhook_secret: Secret for verifying webhook signatures. - If None, reads from OPENAI_WEBHOOK_SECRET environment variable. - base_url: Override the default API base URL (https://api.openai.com/v1). - If None, reads from OPENAI_BASE_URL environment variable. - websocket_base_url: Base URL for WebSocket connections (realtime API). - If None, defaults to base_url with wss:// scheme. - timeout: Request timeout in seconds. Can be a float or Timeout object. - Default is NOT_GIVEN which uses library defaults. - max_retries: Maximum number of retry attempts for failed requests. - Default is 2. - default_headers: Additional HTTP headers to include in all requests. - default_query: Additional query parameters to include in all requests. - http_client: Custom httpx.AsyncClient instance for advanced HTTP configuration. - _strict_response_validation: Enable strict API response validation. - Raises APIResponseValidationError if API returns invalid data. - - Raises: - OpenAIError: If api_key is not provided and OPENAI_API_KEY is not set. - """ -``` - -Usage example: - -```python -import asyncio -from openai import AsyncOpenAI - -async def main(): - # Basic initialization - client = AsyncOpenAI(api_key="sk-...") - - # Using environment variable - client = AsyncOpenAI() - - # Make async API calls - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) - print(response.choices[0].message.content) - - # Custom async HTTP client - import httpx - - async with httpx.AsyncClient( - limits=httpx.Limits(max_keepalive_connections=10) - ) as http_client: - client = AsyncOpenAI( - api_key="sk-...", - http_client=http_client - ) - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) - -asyncio.run(main()) -``` - -### Azure OpenAI Client - -Creates a synchronous client for Azure OpenAI Service with Azure-specific authentication and configuration. - -```python { .api } -class AzureOpenAI: - """ - Synchronous client for Azure OpenAI Service. - - Automatically infers credentials from environment variables: - - api_key from AZURE_OPENAI_API_KEY - - azure_ad_token from AZURE_OPENAI_AD_TOKEN - - azure_endpoint from AZURE_OPENAI_ENDPOINT - - api_version from OPENAI_API_VERSION - - organization from OPENAI_ORG_ID - - project from OPENAI_PROJECT_ID - """ - - def __init__( - self, - *, - api_version: str | None = None, - azure_endpoint: str | None = None, - azure_deployment: str | None = None, - api_key: str | Callable[[], str] | None = None, - azure_ad_token: str | None = None, - azure_ad_token_provider: Callable[[], str] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - websocket_base_url: str | httpx.URL | None = None, - base_url: str | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: - """ - Construct a new synchronous Azure OpenAI client instance. - - Authentication is mutually exclusive - provide only ONE of: - - api_key - - azure_ad_token - - azure_ad_token_provider - - Args: - api_version: Azure OpenAI API version (e.g., "2024-02-15-preview"). - If None, reads from OPENAI_API_VERSION environment variable. - azure_endpoint: Azure resource endpoint (e.g., "https://example.azure.openai.com/"). - If None, reads from AZURE_OPENAI_ENDPOINT environment variable. - azure_deployment: Model deployment name. When provided with azure_endpoint, - sets base URL to include /deployments/{azure_deployment}. - Not supported with Assistants APIs. - api_key: Azure OpenAI API key. - If None, reads from AZURE_OPENAI_API_KEY environment variable. - azure_ad_token: Azure Active Directory (Entra ID) access token. - If None, reads from AZURE_OPENAI_AD_TOKEN environment variable. - azure_ad_token_provider: Function returning Azure AD token, invoked per request. - Useful for automatic token refresh. - organization: OpenAI organization ID (optional for Azure). - If None, reads from OPENAI_ORG_ID environment variable. - project: OpenAI project ID (optional for Azure). - If None, reads from OPENAI_PROJECT_ID environment variable. - webhook_secret: Secret for verifying webhook signatures. - websocket_base_url: Base URL for WebSocket connections. - base_url: Override the base URL (alternative to azure_endpoint). - timeout: Request timeout in seconds. - Default is NOT_GIVEN which uses library defaults. - max_retries: Maximum number of retry attempts for failed requests. - Default is 2. - default_headers: Additional HTTP headers to include in all requests. - default_query: Additional query parameters to include in all requests. - http_client: Custom httpx.Client instance for advanced HTTP configuration. - _strict_response_validation: Enable strict API response validation. - - Raises: - OpenAIError: If no authentication credentials are provided. - MutuallyExclusiveAuthError: If multiple authentication methods are provided. - """ -``` - -Usage example: - -```python -from openai import AzureOpenAI - -# Using API key authentication -client = AzureOpenAI( - api_key="your-azure-api-key", - azure_endpoint="https://your-resource.azure.openai.com/", - api_version="2024-02-15-preview" -) - -# Using environment variables -# Set AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, OPENAI_API_VERSION -client = AzureOpenAI() - -# With deployment name (for model-specific endpoints) -client = AzureOpenAI( - api_key="your-azure-api-key", - azure_endpoint="https://your-resource.azure.openai.com/", - azure_deployment="gpt-4-deployment", - api_version="2024-02-15-preview" -) - -# Using Azure AD (Entra ID) authentication -client = AzureOpenAI( - azure_ad_token="your-azure-ad-token", - azure_endpoint="https://your-resource.azure.openai.com/", - api_version="2024-02-15-preview" -) - -# Using Azure AD with automatic token refresh -def get_azure_ad_token() -> str: - # Implement token retrieval/refresh logic - from azure.identity import DefaultAzureCredential - credential = DefaultAzureCredential() - token = credential.get_token("https://cognitiveservices.azure.com/.default") - return token.token - -client = AzureOpenAI( - azure_ad_token_provider=get_azure_ad_token, - azure_endpoint="https://your-resource.azure.openai.com/", - api_version="2024-02-15-preview" -) - -# Make API calls (same interface as OpenAI) -response = client.chat.completions.create( - model="gpt-4", # Uses deployment if azure_deployment was set - messages=[{"role": "user", "content": "Hello!"}] -) -``` - -### Async Azure OpenAI Client - -Creates an asynchronous client for Azure OpenAI Service. - -```python { .api } -class AsyncAzureOpenAI: - """ - Asynchronous client for Azure OpenAI Service. - - Automatically infers credentials from environment variables: - - api_key from AZURE_OPENAI_API_KEY - - azure_ad_token from AZURE_OPENAI_AD_TOKEN - - azure_endpoint from AZURE_OPENAI_ENDPOINT - - api_version from OPENAI_API_VERSION - - organization from OPENAI_ORG_ID - - project from OPENAI_PROJECT_ID - """ - - def __init__( - self, - *, - azure_endpoint: str | None = None, - azure_deployment: str | None = None, - api_version: str | None = None, - api_key: str | Callable[[], Awaitable[str]] | None = None, - azure_ad_token: str | None = None, - azure_ad_token_provider: Callable[[], str | Awaitable[str]] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: - """ - Construct a new asynchronous Azure OpenAI client instance. - - Authentication is mutually exclusive - provide only ONE of: - - api_key - - azure_ad_token - - azure_ad_token_provider - - Args: - api_version: Azure OpenAI API version (e.g., "2024-02-15-preview"). - If None, reads from OPENAI_API_VERSION environment variable. - azure_endpoint: Azure resource endpoint (e.g., "https://example.azure.openai.com/"). - If None, reads from AZURE_OPENAI_ENDPOINT environment variable. - azure_deployment: Model deployment name. When provided with azure_endpoint, - sets base URL to include /deployments/{azure_deployment}. - Not supported with Assistants APIs. - api_key: Azure OpenAI API key. Can be a string, sync callable, or async callable. - If None, reads from AZURE_OPENAI_API_KEY environment variable. - azure_ad_token: Azure Active Directory (Entra ID) access token. - If None, reads from AZURE_OPENAI_AD_TOKEN environment variable. - azure_ad_token_provider: Function returning Azure AD token (sync or async), - invoked per request. Useful for automatic token refresh. - organization: OpenAI organization ID (optional for Azure). - If None, reads from OPENAI_ORG_ID environment variable. - project: OpenAI project ID (optional for Azure). - If None, reads from OPENAI_PROJECT_ID environment variable. - webhook_secret: Secret for verifying webhook signatures. - websocket_base_url: Base URL for WebSocket connections. - base_url: Override the base URL (alternative to azure_endpoint). - timeout: Request timeout in seconds. - Default is NOT_GIVEN which uses library defaults. - max_retries: Maximum number of retry attempts for failed requests. - Default is 2. - default_headers: Additional HTTP headers to include in all requests. - default_query: Additional query parameters to include in all requests. - http_client: Custom httpx.AsyncClient instance for advanced HTTP configuration. - _strict_response_validation: Enable strict API response validation. - - Raises: - OpenAIError: If no authentication credentials are provided. - MutuallyExclusiveAuthError: If multiple authentication methods are provided. - """ -``` - -Usage example: - -```python -import asyncio -from openai import AsyncAzureOpenAI - -async def main(): - # Basic async Azure client - client = AsyncAzureOpenAI( - api_key="your-azure-api-key", - azure_endpoint="https://your-resource.azure.openai.com/", - api_version="2024-02-15-preview" - ) - - # Async Azure AD token provider - async def get_azure_ad_token() -> str: - # Implement async token retrieval - from azure.identity.aio import DefaultAzureCredential - credential = DefaultAzureCredential() - token = await credential.get_token("https://cognitiveservices.azure.com/.default") - return token.token - - client = AsyncAzureOpenAI( - azure_ad_token_provider=get_azure_ad_token, - azure_endpoint="https://your-resource.azure.openai.com/", - api_version="2024-02-15-preview" - ) - - # Make async API calls - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) - print(response.choices[0].message.content) - -asyncio.run(main()) -``` - -## Types - -```python { .api } -from typing import Callable, Mapping, Awaitable -import httpx - -# Timeout configuration -Timeout = float | httpx.Timeout - -# Sentinel for omitted parameters -class NotGiven: - """Sentinel value indicating a parameter was not provided.""" - -NOT_GIVEN: NotGiven - -# Azure AD token providers -AzureADTokenProvider = Callable[[], str] -AsyncAzureADTokenProvider = Callable[[], str | Awaitable[str]] -``` - -## Client Utility Methods - -All clients provide utility methods for creating client copies with modified configuration and accessing raw HTTP responses. - -### Copy Client with Modified Options - -Create a new client instance reusing the same options with optional overrides. - -```python { .api } -def copy( - self, - *, - api_key: str | Callable[[], str] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - websocket_base_url: str | httpx.URL | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | Omit = omit, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, -) -> OpenAI: - """ - Create a new client instance re-using the same options with optional overriding. - - Args: - api_key: Override API key - organization: Override organization - project: Override project - webhook_secret: Override webhook secret - websocket_base_url: Override WebSocket base URL - base_url: Override base URL - timeout: Override timeout - http_client: Override HTTP client - max_retries: Override max retries - default_headers: Merge these headers with existing default headers - set_default_headers: Replace all default headers with these - default_query: Merge these query params with existing defaults - set_default_query: Replace all default query params with these - - Returns: - New client instance with merged configuration. - - Note: - - default_headers and set_default_headers are mutually exclusive - - default_query and set_default_query are mutually exclusive - """ - -# Alias for nicer inline usage -with_options = copy -``` - -Usage example: - -```python -from openai import OpenAI - -# Create base client -client = OpenAI(api_key="sk-...", timeout=30) - -# Create a copy with longer timeout for specific operations -slow_client = client.copy(timeout=120) - -# Or use the with_options alias for inline usage -response = client.with_options(timeout=60).chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Complex query..."}] -) - -# Create a copy with additional headers -custom_client = client.copy( - default_headers={"X-Request-ID": "abc123"} -) - -# Replace all headers -new_client = client.copy( - set_default_headers={"X-Custom": "value"} -) -``` - -### Access Raw HTTP Responses - -Access the underlying `httpx.Response` object for any API call. - -```python { .api } -@property -def with_raw_response(self) -> OpenAIWithRawResponse: - """ - Property that returns a client wrapper providing access to raw HTTP responses. - - All API methods on this wrapper return a response object with: - - parsed: The normally returned parsed response object - - http_response: The raw httpx.Response object - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Access raw HTTP response -response = client.with_raw_response.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] -) - -# Access parsed response -completion = response.parsed -print(completion.choices[0].message.content) - -# Access raw HTTP response details -http_response = response.http_response -print(f"Status: {http_response.status_code}") -print(f"Headers: {dict(http_response.headers)}") -print(f"Request ID: {http_response.headers.get('x-request-id')}") -``` - -### Stream HTTP Responses - -Stream responses without loading them into memory, useful for large responses like file downloads. - -```python { .api } -@property -def with_streaming_response(self) -> OpenAIWithStreamedResponse: - """ - Property that returns a client wrapper for streaming HTTP responses. - - Instead of loading the entire response into memory, returns a response - object that can be iterated over in chunks. - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Stream file content without loading into memory -response = client.with_streaming_response.files.content("file-abc123") - -# Stream chunks -for chunk in response.iter_bytes(chunk_size=8192): - process_chunk(chunk) - -# Or write directly to file -with open("output.txt", "wb") as f: - for chunk in response.iter_bytes(): - f.write(chunk) - -# Also works with other endpoints -response = client.with_streaming_response.audio.speech.create( - model="tts-1", - voice="alloy", - input="Hello world" -) - -with open("speech.mp3", "wb") as f: - for chunk in response.iter_bytes(): - f.write(chunk) -``` - -Async clients have the same methods: - -```python -from openai import AsyncOpenAI - -client = AsyncOpenAI() - -# Raw response (async) -response = await client.with_raw_response.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] -) - -# Streaming response (async) -response = await client.with_streaming_response.files.content("file-abc123") -async for chunk in response.iter_bytes(): - process_chunk(chunk) - -# Copy/with_options (async) -slow_client = client.copy(timeout=120) -``` - -## Client Properties - -All clients (OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI) expose the following resource properties for accessing API endpoints: - -```python { .api } -# Core resources -client.chat # Chat completions -client.completions # Legacy text completions -client.embeddings # Vector embeddings -client.audio # Audio transcription, translation, TTS -client.images # Image generation -client.videos # Video generation -client.files # File management -client.models # Model information -client.moderations # Content moderation -client.fine_tuning # Fine-tuning jobs -client.batches # Batch processing -client.vector_stores # Vector stores -client.uploads # Multipart uploads -client.responses # Responses API -client.realtime # Realtime API -client.webhooks # Webhook verification -client.beta # Beta features (Assistants, Threads, etc.) -client.conversations # Conversations API -client.evals # Evaluations -client.containers # Containers -``` - -## Error Handling - -All client initialization errors inherit from OpenAIError: - -```python { .api } -class OpenAIError(Exception): - """Base exception for all OpenAI errors.""" - -class MutuallyExclusiveAuthError(OpenAIError): - """Raised when multiple authentication methods are provided to Azure clients.""" -``` - -Common initialization errors: - -```python -from openai import OpenAI, OpenAIError, AzureOpenAI, MutuallyExclusiveAuthError - -# Handle missing API key -try: - client = OpenAI() # No api_key and OPENAI_API_KEY not set -except OpenAIError as e: - print(f"Missing API key: {e}") - -# Handle Azure authentication conflicts -try: - client = AzureOpenAI( - api_key="key", - azure_ad_token="token" # Can't provide both - ) -except MutuallyExclusiveAuthError as e: - print(f"Auth conflict: {e}") -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/completions.md b/.tessl/tiles/tessl/pypi-openai/docs/completions.md deleted file mode 100644 index 3b334d8ba..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/completions.md +++ /dev/null @@ -1,292 +0,0 @@ -# Text Completions - -Generate text completions using legacy completion models. This API is superseded by Chat Completions for most use cases, but remains available for compatibility with specific completion-optimized models. - -## Capabilities - -### Create Completion - -Generate text completion for a given prompt. - -```python { .api } -def create( - self, - *, - model: str, - prompt: str | list[str] | list[int] | list[list[int]], - best_of: int | Omit = omit, - echo: bool | Omit = omit, - frequency_penalty: float | Omit = omit, - logit_bias: dict[str, int] | Omit = omit, - logprobs: int | Omit = omit, - max_tokens: int | Omit = omit, - n: int | Omit = omit, - presence_penalty: float | Omit = omit, - seed: int | Omit = omit, - stop: str | list[str] | Omit = omit, - stream: bool | Omit = omit, - stream_options: dict | None | Omit = omit, - suffix: str | Omit = omit, - temperature: float | Omit = omit, - top_p: float | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Completion | Stream[Completion]: - """ - Generate text completion for a prompt. - - NOTE: Most use cases are better served by Chat Completions API. - This endpoint is primarily for completion-specific models like gpt-3.5-turbo-instruct. - - Args: - model: Model ID. Supported models: - - "gpt-3.5-turbo-instruct": Instruction-tuned model optimized for completions - - "davinci-002": Legacy Davinci model - - "babbage-002": Legacy Babbage model - - prompt: Text prompt(s) to complete. Can be: - - Single string: "Once upon a time" - - List of strings: ["Story 1", "Story 2"] - - Token array: [123, 456, 789] - - List of token arrays: [[123, 456], [789, 012]] - - best_of: Generates best_of completions server-side, returns best one. - Must be greater than n. Used with temperature for better quality. - Cannot be used with stream. - - echo: If true, echoes the prompt in addition to completion. - - frequency_penalty: Number between -2.0 and 2.0. Penalizes tokens based on - their frequency in the text so far. Default 0. - - logit_bias: Modify token probabilities. Maps token IDs to bias values - from -100 to 100. - - logprobs: Include log probabilities of the most likely tokens. - Returns logprobs number of most likely tokens per position. - Maximum value is 5. - - max_tokens: Maximum tokens to generate. Default 16. - - n: Number of completions to generate. Default 1. - - presence_penalty: Number between -2.0 and 2.0. Penalizes tokens based on - whether they appear in the text so far. Default 0. - - seed: For deterministic sampling (Beta). Same seed + parameters should - return same result. Not guaranteed. - - stop: Up to 4 sequences where generation stops. Can be string or list. - - stream: If true, returns SSE stream of partial completions. - - stream_options: Streaming configuration. Accepts dict with: - - "include_usage": bool - If true, includes token usage in final chunk - - "include_obfuscation": bool - If true (default), adds random characters - to obfuscation field on streaming delta events to normalize payload sizes - as mitigation for side-channel attacks. Set to false to optimize bandwidth. - - suffix: Text that comes after the completion. Useful for inserting text. - - temperature: Sampling temperature between 0 and 2. Higher values make - output more random. Default 1. Alter this or top_p, not both. - - top_p: Nucleus sampling parameter between 0 and 1. Default 1. - Alter this or temperature, not both. - - user: Unique end-user identifier for abuse monitoring. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Completion: If stream=False (default), returns complete response. - Stream[Completion]: If stream=True, returns streaming response. - - Raises: - BadRequestError: Invalid parameters - AuthenticationError: Invalid API key - RateLimitError: Rate limit exceeded - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic completion -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Once upon a time", - max_tokens=50 -) - -print(response.choices[0].text) - -# Multiple prompts -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt=[ - "The capital of France is", - "The largest ocean on Earth is" - ], - max_tokens=10 -) - -for i, choice in enumerate(response.choices): - print(f"Completion {i + 1}: {choice.text}") - -# With suffix for text insertion -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="def calculate_sum(a, b):\n \"\"\"", - suffix="\n return a + b", - max_tokens=50 -) - -print(response.choices[0].text) - -# With logprobs -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="The weather today is", - max_tokens=5, - logprobs=2, - echo=True -) - -# Access log probabilities -for token, logprob in zip(response.choices[0].logprobs.tokens, - response.choices[0].logprobs.token_logprobs): - print(f"Token: {token}, Log Prob: {logprob}") - -# Streaming completion -stream = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Write a short poem about coding:", - max_tokens=100, - stream=True -) - -for chunk in stream: - if chunk.choices[0].text: - print(chunk.choices[0].text, end="", flush=True) - -# With best_of for higher quality -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Explain quantum computing in simple terms:", - max_tokens=100, - best_of=3, - n=1, - temperature=0.8 -) - -# With stop sequences -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="List three colors:\n1.", - max_tokens=50, - stop=["\n4.", "\n\n"] -) - -# Deterministic completion with seed -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Generate a random number:", - seed=42, - max_tokens=10 -) -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Completion(BaseModel): - """Completion response.""" - id: str - choices: list[CompletionChoice] - created: int - model: str - object: Literal["text_completion"] - system_fingerprint: str | None - usage: CompletionUsage | None - -class CompletionChoice(BaseModel): - """Single completion choice.""" - finish_reason: Literal["stop", "length", "content_filter"] - index: int - logprobs: Logprobs | None - text: str - -class Logprobs(BaseModel): - """Log probability information.""" - text_offset: list[int] - token_logprobs: list[float | None] - tokens: list[str] - top_logprobs: list[dict[str, float]] | None - -class CompletionUsage(BaseModel): - """Token usage statistics.""" - completion_tokens: int - prompt_tokens: int - total_tokens: int - -# Stream wrapper -class Stream(Generic[T]): - def __iter__(self) -> Iterator[T]: ... - def __next__(self) -> T: ... - def __enter__(self) -> Stream[T]: ... - def __exit__(self, *args) -> None: ... -``` - -## Migration to Chat Completions - -For most use cases, Chat Completions API is recommended: - -```python -# Legacy Completions -response = client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Translate to French: Hello" -) - -# Equivalent Chat Completion -response = client.chat.completions.create( - model="gpt-3.5-turbo", - messages=[ - {"role": "user", "content": "Translate to French: Hello"} - ] -) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def complete_text(): - client = AsyncOpenAI() - - response = await client.completions.create( - model="gpt-3.5-turbo-instruct", - prompt="Once upon a time", - max_tokens=50 - ) - - return response.choices[0].text - -text = asyncio.run(complete_text()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/containers.md b/.tessl/tiles/tessl/pypi-openai/docs/containers.md deleted file mode 100644 index cbe63e970..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/containers.md +++ /dev/null @@ -1,641 +0,0 @@ -# Containers - -Create and manage isolated file storage containers for organizing and sharing files. Containers provide a way to group related files together with configurable expiration policies. - -## Capabilities - -### Create Container - -Create a new container for file storage. - -```python { .api } -def create( - self, - *, - name: str, - expires_after: dict | Omit = omit, - file_ids: list[str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Container: - """ - Create a container. - - Args: - name: Name of the container to create. Used for identification. - - expires_after: Container expiration time in seconds relative to anchor time. - Example: {"anchor": "created_at", "days": 7} expires 7 days after creation. - Default: containers persist until manually deleted. - - file_ids: IDs of files to copy into the container at creation. - Files are copied, not moved - originals remain accessible. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Container: Created container object with unique ID. - - Notes: - - Containers provide isolated file storage - - Files are copied into containers, not moved - - Expired containers are automatically cleaned up - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create empty container -container = client.containers.create( - name="project-resources" -) -print(f"Created container: {container.id}") - -# Create with files -container = client.containers.create( - name="training-data", - file_ids=["file-abc123", "file-def456"] -) - -# Create with expiration -container = client.containers.create( - name="temp-files", - expires_after={ - "anchor": "created_at", - "days": 1 - } -) - -# Create for specific project -container = client.containers.create( - name="customer-support-logs", - file_ids=["file-log1", "file-log2", "file-log3"], - expires_after={ - "anchor": "created_at", - "days": 30 - } -) -``` - -### Retrieve Container - -Get a container by its ID. - -```python { .api } -def retrieve( - self, - container_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Container: - """ - Retrieve a container. - - Args: - container_id: ID of the container to retrieve. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Container: Container object with metadata and file information. - """ -``` - -### List Containers - -List all containers with pagination support. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Container]: - """ - List containers. - - Args: - after: Cursor for pagination. Object ID defining your place in the list. - For instance, if you receive 100 objects ending with obj_foo, - use after=obj_foo to fetch the next page. - - limit: Maximum number of objects to return. Range: 1-100. Default: 20. - - order: Sort order by created_at timestamp. - - "asc": Ascending (oldest first) - - "desc": Descending (newest first, default) - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[Container]: Paginated list of containers. - Supports iteration: for container in client.containers.list(): ... - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# List all containers -for container in client.containers.list(): - print(f"{container.name}: {container.id}") - -# List with pagination -page = client.containers.list(limit=10) -for container in page: - print(f"{container.name} - Created: {container.created_at}") - -# Get next page -if page.has_more: - next_page = client.containers.list( - limit=10, - after=page.data[-1].id - ) - -# List in ascending order (oldest first) -for container in client.containers.list(order="asc"): - print(container.name) -``` - -### Delete Container - -Delete a container. - -```python { .api } -def delete( - self, - container_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> None: - """ - Delete a container. - - Args: - container_id: ID of the container to delete. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - None: No content returned on successful deletion. - - Notes: - - Deletion is permanent and cannot be undone - - Files within the container are not deleted - - Original files remain accessible outside the container - """ -``` - -### Container Files - -Manage files within a container. - -```python { .api } -# Access via client.containers.files - -def create( - self, - container_id: str, - *, - file: FileTypes | Omit = omit, - file_id: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FileCreateResponse: - """ - Add a file to a container. - - Args: - container_id: ID of the container. - - file: File content to upload directly. Provide either this OR file_id. - - file_id: ID of an existing file to add. File is copied into container. - Provide either this OR file. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FileCreateResponse: File reference within the container. - - Notes: - - Provide either `file` (for direct upload) OR `file_id` (for reference) - - When using file_id, file is copied, not moved - - Original file remains accessible - - Same file can be in multiple containers - """ - -def retrieve( - self, - container_id: str, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ContainerFile: - """ - Retrieve a file from a container. - - Args: - container_id: ID of the container. - file_id: ID of the file to retrieve. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ContainerFile: File information and metadata. - """ - -def list( - self, - container_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[ContainerFile]: - """ - List files in a container. - - Args: - container_id: ID of the container. - - after: Cursor for pagination. - - limit: Maximum number of files to return. Range: 1-100. Default: 20. - - order: Sort order by created_at timestamp ("asc" or "desc"). - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[ContainerFile]: Paginated list of files. - """ - -def delete( - self, - container_id: str, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> None: - """ - Remove a file from a container. - - Args: - container_id: ID of the container. - file_id: ID of the file to remove. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - None: No content returned on successful removal. - - Notes: - - Removes file from container only - - Original file is not deleted - - File remains accessible outside container - """ -``` - -### Container File Content - -Retrieve the binary content of a file stored in a container. - -```python { .api } -# Access via client.containers.files.content - -def retrieve( - self, - file_id: str, - *, - container_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> HttpxBinaryResponseContent: - """ - Retrieve the binary content of a container file. - - Args: - file_id: ID of the file to retrieve content from. - - container_id: ID of the container containing the file. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - HttpxBinaryResponseContent: Binary content of the file. - Can be written to disk or processed in memory. - - Notes: - - Returns raw binary data - - Suitable for downloading file contents - - Use .read() or .iter_bytes() to access content - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Retrieve file content from container -content = client.containers.files.content.retrieve( - container_id="container-abc123", - file_id="file-def456" -) - -# Save to disk -with open("downloaded_file.pdf", "wb") as f: - f.write(content.read()) - -# Or process in chunks -for chunk in content.iter_bytes(chunk_size=8192): - # Process chunk - pass -``` - -Complete workflow example: - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Upload some files first -file1 = client.files.create( - file=open("document1.pdf", "rb"), - purpose="assistants" -) - -file2 = client.files.create( - file=open("document2.pdf", "rb"), - purpose="assistants" -) - -# 2. Create container with one file -container = client.containers.create( - name="project-docs", - file_ids=[file1.id], - expires_after={ - "anchor": "created_at", - "days": 7 - } -) - -print(f"Created container: {container.id}") - -# 3. Add another file to container -client.containers.files.create( - container_id=container.id, - file_id=file2.id -) - -# 4. List all files in container -print("Files in container:") -for file in client.containers.files.list(container_id=container.id): - print(f" - {file.id}") - -# 5. Retrieve specific file from container -file_info = client.containers.files.retrieve( - container_id=container.id, - file_id=file1.id -) -print(f"File info: {file_info}") - -# 6. Remove a file from container -client.containers.files.delete( - container_id=container.id, - file_id=file1.id -) - -# 7. Verify removal -remaining_files = client.containers.files.list(container_id=container.id) -print(f"Remaining files: {len(remaining_files.data)}") - -# 8. List all containers -print("\nAll containers:") -for c in client.containers.list(): - print(f" {c.name} ({c.id})") - -# 9. Clean up - delete container -client.containers.delete(container_id=container.id) - -# Note: Original files still exist -retrieved_file = client.files.retrieve(file_id=file1.id) -print(f"Original file still exists: {retrieved_file.id}") -``` - -Organizing files by project: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create containers for different projects -ml_container = client.containers.create( - name="machine-learning-project" -) - -web_container = client.containers.create( - name="web-development-project" -) - -# Upload and organize files -ml_file = client.files.create( - file=open("model_training.py", "rb"), - purpose="assistants" -) - -web_file = client.files.create( - file=open("api_docs.md", "rb"), - purpose="assistants" -) - -# Add files to appropriate containers -client.containers.files.create( - container_id=ml_container.id, - file_id=ml_file.id -) - -client.containers.files.create( - container_id=web_container.id, - file_id=web_file.id -) - -# Later: access files by project -print("ML Project Files:") -for file in client.containers.files.list(container_id=ml_container.id): - print(f" - {file.id}") - -print("Web Project Files:") -for file in client.containers.files.list(container_id=web_container.id): - print(f" - {file.id}") -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def manage_containers(): - client = AsyncOpenAI() - - # Create container - container = await client.containers.create( - name="async-project", - expires_after={"anchor": "created_at", "days": 1} - ) - - # Add file - await client.containers.files.create( - container_id=container.id, - file_id="file-abc123" - ) - - # List files - async for file in await client.containers.files.list( - container_id=container.id - ): - print(f"File: {file.id}") - - # Delete container - await client.containers.delete(container_id=container.id) - -asyncio.run(manage_containers()) -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Container(BaseModel): - """Container object for file organization.""" - id: str - created_at: int - name: str - object: Literal["container"] - expires_at: int | None # Present if expiration configured - file_count: int | None # Number of files in container - -class ContainerFile(BaseModel): - """File reference within a container.""" - id: str - container_id: str - file_id: str - created_at: int - object: Literal["container.file"] - -class SyncCursorPage[T]: - """Cursor-based pagination.""" - data: list[T] - has_more: bool - def __iter__(self) -> Iterator[T]: ... - -class Omit: - """Sentinel value for omitted parameters.""" - -class HttpxBinaryResponseContent: - """Binary response content from HTTP requests.""" - def read(self) -> bytes: ... - def iter_bytes(self, chunk_size: int = ...) -> Iterator[bytes]: ... -``` - -## Access Pattern - -```python -# Synchronous -from openai import OpenAI -client = OpenAI() -client.containers.create(...) -client.containers.retrieve(...) -client.containers.list(...) -client.containers.delete(...) -client.containers.files.create(...) -client.containers.files.retrieve(...) -client.containers.files.list(...) -client.containers.files.delete(...) -client.containers.files.content.retrieve(...) - -# Asynchronous -from openai import AsyncOpenAI -client = AsyncOpenAI() -await client.containers.create(...) -await client.containers.retrieve(...) -await client.containers.list(...) -await client.containers.delete(...) -await client.containers.files.create(...) -await client.containers.files.retrieve(...) -await client.containers.files.list(...) -await client.containers.files.delete(...) -await client.containers.files.content.retrieve(...) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/conversations.md b/.tessl/tiles/tessl/pypi-openai/docs/conversations.md deleted file mode 100644 index c9d14e156..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/conversations.md +++ /dev/null @@ -1,599 +0,0 @@ -# Conversations - -Create and manage conversations for structured multi-turn interactions. Conversations provide a lightweight alternative to Threads for managing conversational context with items that can include user inputs, system messages, and function outputs. - -## Capabilities - -### Create Conversation - -Create a new conversation with optional initial items. - -```python { .api } -def create( - self, - *, - items: Iterable[dict] | None | Omit = omit, - metadata: dict[str, str] | None | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: - """ - Create a conversation. - - Args: - items: Initial items to include in the conversation context. - You may add up to 20 items at a time. Each item can be: - - Message: {"type": "message", "role": "user", "content": "..."} - - System: {"type": "message", "role": "system", "content": "..."} - - Function output: {"type": "function_call_output", "call_id": "...", "output": "..."} - - metadata: Up to 16 key-value pairs for storing additional information. - Keys max 64 characters, values max 512 characters. - Useful for storing user IDs, session info, etc. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Conversation: Created conversation object with unique ID. - - Notes: - - Conversations are lightweight compared to Threads - - No automatic message persistence - items must be explicitly managed - - Use for scenarios requiring explicit context control - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create empty conversation -conversation = client.conversations.create() -print(f"Created conversation: {conversation.id}") - -# Create with initial items -conversation = client.conversations.create( - items=[ - { - "type": "message", - "role": "system", - "content": "You are a helpful coding assistant." - }, - { - "type": "message", - "role": "user", - "content": "How do I reverse a string in Python?" - } - ], - metadata={ - "user_id": "user_123", - "session": "session_abc" - } -) - -# Create with metadata -conversation = client.conversations.create( - metadata={ - "purpose": "customer_support", - "language": "en", - "priority": "high" - } -) -``` - -### Retrieve Conversation - -Get a conversation by its ID. - -```python { .api } -def retrieve( - self, - conversation_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: - """ - Get a conversation. - - Args: - conversation_id: ID of the conversation to retrieve. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Conversation: Conversation object with current metadata and items. - """ -``` - -### Update Conversation - -Update conversation metadata. - -```python { .api } -def update( - self, - conversation_id: str, - *, - metadata: dict[str, str] | None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: - """ - Update a conversation. - - Args: - conversation_id: ID of the conversation to update. - - metadata: New metadata dictionary. Replaces all existing metadata. - Up to 16 key-value pairs, keys max 64 chars, values max 512 chars. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Conversation: Updated conversation object. - - Notes: - - Metadata is completely replaced, not merged - - To preserve existing metadata, retrieve first then update - """ -``` - -### Delete Conversation - -Delete a conversation. - -```python { .api } -def delete( - self, - conversation_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationDeleted: - """ - Delete a conversation. - - Args: - conversation_id: ID of the conversation to delete. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ConversationDeleted: Deletion confirmation with ID and status. - - Notes: - - Items in the conversation will not be deleted - - Deletion is permanent and cannot be undone - - Items remain accessible if you have their IDs - """ -``` - -### Conversation Items - -Manage items within a conversation. - -```python { .api } -# Access via client.conversations.items - -def create( - self, - conversation_id: str, - *, - items: Iterable[dict], - include: list[Literal[ - "web_search_call.action.sources", - "code_interpreter_call.outputs", - "computer_call_output.output.image_url", - "file_search_call.results", - "message.input_image.image_url", - "message.output_text.logprobs", - "reasoning.encrypted_content" - ]] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationItemList: - """ - Create items in a conversation. - - Args: - conversation_id: ID of the conversation. - - items: The items to add to the conversation. You may add up to 20 items at a time. - Each item is a dict with one of these structures: - - Message: {"type": "message", "role": "user"|"system"|"developer", "content": [...]} - - Function call output: {"type": "function_call_output", "call_id": "...", "output": "..."} - - Computer call output: {"type": "computer_call_output", "call_id": "...", "output": {...}} - - Shell call output: {"type": "shell_call_output", "call_id": "...", "output": [...]} - - And many other item types (see ResponseInputItemParam for full list) - - include: Additional fields to include in the response. Options: - - "web_search_call.action.sources": Include web search sources - - "code_interpreter_call.outputs": Include code execution outputs - - "computer_call_output.output.image_url": Include computer call images - - "file_search_call.results": Include file search results - - "message.input_image.image_url": Include input message images - - "message.output_text.logprobs": Include logprobs with assistant messages - - "reasoning.encrypted_content": Include encrypted reasoning tokens - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ConversationItemList: List of created items with IDs and timestamps. - """ - -def retrieve( - self, - item_id: str, - *, - conversation_id: str, - include: list[Literal[ - "web_search_call.action.sources", - "code_interpreter_call.outputs", - "computer_call_output.output.image_url", - "file_search_call.results", - "message.input_image.image_url", - "message.output_text.logprobs", - "reasoning.encrypted_content" - ]] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationItem: - """ - Retrieve a specific item from a conversation. - - Args: - item_id: ID of the item to retrieve. - conversation_id: ID of the conversation. - - include: Additional fields to include in the response. See create() for available options. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ConversationItem: Item with full content and metadata. - """ - -def delete( - self, - item_id: str, - *, - conversation_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: - """ - Delete an item from a conversation. - - Args: - item_id: ID of the item to delete. - conversation_id: ID of the conversation. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Conversation: Updated conversation after item deletion. - - Notes: - - Deletion is permanent - - Returns the conversation object (not a deletion confirmation) - """ - -def list( - self, - conversation_id: str, - *, - after: str | Omit = omit, - include: list[Literal[ - "web_search_call.action.sources", - "code_interpreter_call.outputs", - "computer_call_output.output.image_url", - "file_search_call.results", - "message.input_image.image_url", - "message.output_text.logprobs", - "reasoning.encrypted_content" - ]] | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncConversationCursorPage[ConversationItem]: - """ - List items in a conversation. - - Args: - conversation_id: ID of the conversation. - - after: Cursor for pagination. Return items after this ID. - - include: Additional fields to include in the response. See create() for available options. - - limit: Maximum number of items to return (1-100). Default 20. - - order: Sort order by creation time: - - "asc": Ascending (oldest first) - - "desc": Descending (newest first, default) - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncConversationCursorPage[ConversationItem]: Paginated list of items. - """ -``` - -Complete workflow example: - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Create conversation -conversation = client.conversations.create( - metadata={"user_id": "user_123"} -) - -# 2. Add system and user messages (batch create) -items_response = client.conversations.items.create( - conversation_id=conversation.id, - items=[ - { - "type": "message", - "role": "system", - "content": [{"type": "text", "text": "You are a helpful Python tutor."}] - }, - { - "type": "message", - "role": "user", - "content": [{"type": "text", "text": "How do I read a file in Python?"}] - } - ] -) - -system_item = items_response.data[0] -user_item = items_response.data[1] - -# 4. List all items -items = client.conversations.items.list( - conversation_id=conversation.id, - order="asc" -) - -for item in items: - print(f"{item.role}: {item.content}") - -# 5. Add assistant response -assistant_response = client.conversations.items.create( - conversation_id=conversation.id, - items=[ - { - "type": "message", - "role": "assistant", - "content": [{"type": "text", "text": "You can read a file using: with open('file.txt', 'r') as f: content = f.read()"}] - } - ] -) -assistant_item = assistant_response.data[0] - -# 6. Update conversation metadata -client.conversations.update( - conversation_id=conversation.id, - metadata={ - "user_id": "user_123", - "status": "resolved", - "topic": "file_io" - } -) - -# 7. Get updated conversation -updated = client.conversations.retrieve(conversation_id=conversation.id) -print(f"Metadata: {updated.metadata}") - -# 8. Delete specific item -client.conversations.items.delete( - conversation_id=conversation.id, - item_id=user_item.id -) - -# 9. Clean up - delete conversation -client.conversations.delete(conversation_id=conversation.id) -``` - -Multi-modal conversation example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create conversation with image -conversation = client.conversations.create() - -# Add user message with image -client.conversations.items.create( - conversation_id=conversation.id, - items=[ - { - "type": "message", - "role": "user", - "content": [ - { - "type": "text", - "text": "What's in this image?" - }, - { - "type": "image_url", - "image_url": { - "url": "https://example.com/image.jpg" - } - } - ] - } - ] -) - -# Add assistant response -client.conversations.items.create( - conversation_id=conversation.id, - items=[ - { - "type": "message", - "role": "assistant", - "content": [{"type": "text", "text": "I see a beautiful sunset over mountains."}] - } - ] -) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def manage_conversation(): - client = AsyncOpenAI() - - # Create conversation - conversation = await client.conversations.create( - metadata={"user_id": "user_123"} - ) - - # Add message - await client.conversations.items.create( - conversation_id=conversation.id, - items=[ - { - "type": "message", - "role": "user", - "content": [{"type": "text", "text": "Hello!"}] - } - ] - ) - - # List items - async for item in await client.conversations.items.list( - conversation_id=conversation.id - ): - print(item.content) - - # Delete conversation - await client.conversations.delete(conversation_id=conversation.id) - -asyncio.run(manage_conversation()) -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Conversation(BaseModel): - """Conversation object.""" - id: str - created_at: int - object: Literal["conversation"] - metadata: dict[str, str] | None - -class ConversationItem(BaseModel): - """Item within a conversation (union type - can be message, tool call, or tool output).""" - id: str - conversation_id: str - created_at: int - object: Literal["conversation.item"] - # Type-specific fields vary by item type - # See ResponseInputItemParam for complete type definitions - -class ConversationItemList(BaseModel): - """List of conversation items returned from batch create.""" - data: list[ConversationItem] - object: Literal["list"] - -class ConversationDeleted(BaseModel): - """Deletion confirmation.""" - id: str - deleted: bool - object: Literal["conversation.deleted"] - -class SyncConversationCursorPage[T]: - """Cursor-based pagination for conversation items.""" - data: list[T] - has_more: bool - last_id: str | None - def __iter__(self) -> Iterator[T]: ... - -class Omit: - """Sentinel value for omitted parameters.""" -``` - -## Access Pattern - -```python -# Synchronous -from openai import OpenAI -client = OpenAI() -client.conversations.create(...) -client.conversations.retrieve(...) -client.conversations.update(...) -client.conversations.delete(...) -client.conversations.items.create(...) -client.conversations.items.retrieve(...) -client.conversations.items.delete(...) -client.conversations.items.list(...) - -# Asynchronous -from openai import AsyncOpenAI -client = AsyncOpenAI() -await client.conversations.create(...) -await client.conversations.retrieve(...) -await client.conversations.update(...) -await client.conversations.delete(...) -await client.conversations.items.create(...) -await client.conversations.items.retrieve(...) -await client.conversations.items.delete(...) -await client.conversations.items.list(...) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/embeddings.md b/.tessl/tiles/tessl/pypi-openai/docs/embeddings.md deleted file mode 100644 index 7d1d03ca3..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/embeddings.md +++ /dev/null @@ -1,269 +0,0 @@ -# Embeddings - -Create vector embeddings for text inputs to use in semantic search, clustering, recommendations, and other machine learning applications. Embeddings are numerical representations of text that capture semantic meaning. - -## Capabilities - -### Create Embeddings - -Generate vector embeddings for one or more text inputs. - -```python { .api } -def create( - self, - *, - input: str | list[str] | list[int] | list[list[int]], - model: str | EmbeddingModel, - dimensions: int | Omit = omit, - encoding_format: Literal["float", "base64"] | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> CreateEmbeddingResponse: - """ - Create embedding vectors representing the input text. - - Args: - input: Text to embed. Can be: - - Single string: "Hello world" - - List of strings: ["Hello", "world"] - - Token array: [123, 456, 789] - - List of token arrays: [[123, 456], [789, 012]] - Max 8192 tokens per input, 2048 dimensions max for arrays. - Total limit: 300,000 tokens across all inputs per request. - - model: Embedding model ID. Options: - - "text-embedding-3-large": Most capable, 3072 dimensions - - "text-embedding-3-small": Fast and efficient, 1536 dimensions - - "text-embedding-ada-002": Legacy model, 1536 dimensions - - dimensions: Number of dimensions for output embeddings. - Only supported in text-embedding-3 models. Allows reducing - embedding size for storage/performance. Must be ≤ model's max. - - encoding_format: Output format for embeddings. - - "float": List of floats (default) - - "base64": Base64-encoded bytes for space efficiency - - user: Unique end-user identifier for abuse monitoring. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - CreateEmbeddingResponse: Contains embedding vectors and usage info. - - Raises: - BadRequestError: Invalid input or exceeds token limits - AuthenticationError: Invalid API key - RateLimitError: Rate limit exceeded - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Single text embedding -response = client.embeddings.create( - model="text-embedding-3-small", - input="The quick brown fox jumps over the lazy dog" -) - -embedding = response.data[0].embedding -print(f"Embedding dimension: {len(embedding)}") -print(f"First 5 values: {embedding[:5]}") - -# Multiple texts at once -response = client.embeddings.create( - model="text-embedding-3-small", - input=[ - "Machine learning is fascinating", - "I love natural language processing", - "The weather is nice today" - ] -) - -for i, item in enumerate(response.data): - print(f"Embedding {i}: {len(item.embedding)} dimensions") - -# Using larger model with custom dimensions -response = client.embeddings.create( - model="text-embedding-3-large", - input="Semantic search with embeddings", - dimensions=1024 # Reduce from default 3072 -) - -# Base64 encoding for space efficiency -response = client.embeddings.create( - model="text-embedding-3-small", - input="Compressed embedding format", - encoding_format="base64" -) - -# Decode base64 embedding -import base64 -import array - -encoded = response.data[0].embedding -decoded_bytes = base64.b64decode(encoded) -floats = array.array('f', decoded_bytes) -print(f"Decoded embedding: {list(floats)[:5]}") - -# Token-based input (pre-tokenized) -import tiktoken - -enc = tiktoken.encoding_for_model("text-embedding-3-small") -tokens = enc.encode("Hello world") - -response = client.embeddings.create( - model="text-embedding-3-small", - input=tokens -) - -# Semantic search example -def cosine_similarity(a, b): - import math - dot = sum(x * y for x, y in zip(a, b)) - mag_a = math.sqrt(sum(x * x for x in a)) - mag_b = math.sqrt(sum(y * y for y in b)) - return dot / (mag_a * mag_b) - -# Embed documents -documents = [ - "Python is a programming language", - "Machine learning uses algorithms", - "The cat sat on the mat" -] - -response = client.embeddings.create( - model="text-embedding-3-small", - input=documents -) - -doc_embeddings = [item.embedding for item in response.data] - -# Embed query -query = "Tell me about programming" -query_response = client.embeddings.create( - model="text-embedding-3-small", - input=query -) -query_embedding = query_response.data[0].embedding - -# Find most similar document -similarities = [ - cosine_similarity(query_embedding, doc_emb) - for doc_emb in doc_embeddings -] - -best_match_idx = similarities.index(max(similarities)) -print(f"Most similar document: {documents[best_match_idx]}") -print(f"Similarity score: {similarities[best_match_idx]:.4f}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class CreateEmbeddingResponse(BaseModel): - """Response from embeddings endpoint.""" - data: list[Embedding] - model: str - object: Literal["list"] - usage: Usage - -class Embedding(BaseModel): - """Single embedding vector.""" - embedding: list[float] | str # list[float] for "float", str for "base64" - index: int - object: Literal["embedding"] - -class Usage(BaseModel): - """Token usage information.""" - prompt_tokens: int - total_tokens: int - -# Model type -EmbeddingModel = Literal[ - "text-embedding-3-large", - "text-embedding-3-small", - "text-embedding-ada-002" -] -``` - -## Model Comparison - -| Model | Dimensions | Performance | Use Case | -|-------|-----------|-------------|----------| -| text-embedding-3-large | 3072 (default) | Highest quality | Production semantic search, highest accuracy needed | -| text-embedding-3-small | 1536 (default) | Good quality, faster | General purpose, cost-sensitive applications | -| text-embedding-ada-002 | 1536 (fixed) | Legacy performance | Backwards compatibility | - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Batch similar requests for efficiency -texts = ["text1", "text2", "text3"] # Up to 2048 inputs -response = client.embeddings.create( - model="text-embedding-3-small", - input=texts -) - -# 2. Use dimensions parameter to reduce storage -response = client.embeddings.create( - model="text-embedding-3-large", - input="Sample text", - dimensions=256 # Much smaller than default 3072 -) - -# 3. Handle errors gracefully -try: - response = client.embeddings.create( - model="text-embedding-3-small", - input="x" * 10000 # Too long - ) -except Exception as e: - print(f"Error: {e}") - -# 4. Use base64 for space efficiency in storage -response = client.embeddings.create( - model="text-embedding-3-small", - input="Efficient storage", - encoding_format="base64" -) -# Store base64 string directly, decode when needed -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def get_embeddings(): - client = AsyncOpenAI() - - response = await client.embeddings.create( - model="text-embedding-3-small", - input="Async embedding creation" - ) - - return response.data[0].embedding - -# Run async -embeddings = asyncio.run(get_embeddings()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/evals.md b/.tessl/tiles/tessl/pypi-openai/docs/evals.md deleted file mode 100644 index b957f4c4e..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/evals.md +++ /dev/null @@ -1,775 +0,0 @@ -# Evaluations - -Create and manage evaluations to test model performance with custom testing criteria and data sources. The Evals API enables systematic evaluation of different models and parameters against consistent benchmarks. - -## Capabilities - -### Create Evaluation - -Create an evaluation structure with testing criteria and data source configuration. - -```python { .api } -def create( - self, - *, - data_source_config: dict, - testing_criteria: Iterable[dict], - metadata: dict[str, str] | None | Omit = omit, - name: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Eval: - """ - Create an evaluation for testing model performance. - - Args: - data_source_config: Configuration for the data source used in eval runs. - Dictates the schema of data used in the evaluation. - Example: { - "type": "file", - "file_id": "file-abc123", - "schema": { - "input": {"type": "string"}, - "expected_output": {"type": "string"} - } - } - - testing_criteria: List of graders for all eval runs. Graders can reference - variables in the data source using double curly braces notation like - {{item.variable_name}}. To reference model output, use {{sample.output_text}}. - Example: [ - { - "type": "exact_match", - "expected": "{{item.expected_output}}", - "actual": "{{sample.output_text}}" - }, - { - "type": "contains", - "substring": "{{item.keyword}}", - "text": "{{sample.output_text}}" - } - ] - - metadata: Up to 16 key-value pairs for storing additional information. - Keys max 64 characters, values max 512 characters. - - name: Name of the evaluation for identification. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Eval: Created evaluation object with ID for running evaluations. - - Notes: - - After creating an evaluation, run it on different models/parameters - - See https://platform.openai.com/docs/guides/evals for grader types - - Supported graders: exact_match, contains, llm_judge, custom_code - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create evaluation with testing criteria -eval = client.evals.create( - name="Customer Support Eval", - data_source_config={ - "type": "file", - "file_id": "file-abc123", - "schema": { - "customer_query": {"type": "string"}, - "expected_tone": {"type": "string"}, - "expected_answer": {"type": "string"} - } - }, - testing_criteria=[ - { - "type": "exact_match", - "name": "Answer Correctness", - "expected": "{{item.expected_answer}}", - "actual": "{{sample.output_text}}" - }, - { - "type": "llm_judge", - "name": "Tone Check", - "prompt": "Does the response match the tone: {{item.expected_tone}}?", - "text": "{{sample.output_text}}" - } - ], - metadata={ - "team": "customer-success", - "version": "v1" - } -) - -print(f"Created evaluation: {eval.id}") -``` - -### Retrieve Evaluation - -Get an evaluation by ID. - -```python { .api } -def retrieve( - self, - eval_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Eval: - """ - Retrieve an evaluation by its ID. - - Args: - eval_id: ID of the evaluation to retrieve. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Eval: Evaluation object with full configuration and metadata. - """ -``` - -### Update Evaluation - -Update evaluation properties like name or metadata. - -```python { .api } -def update( - self, - eval_id: str, - *, - metadata: dict[str, str] | None | Omit = omit, - name: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Eval: - """ - Update certain properties of an evaluation. - - Args: - eval_id: ID of the evaluation to update. - - metadata: New metadata key-value pairs. Replaces existing metadata. - Up to 16 pairs, keys max 64 chars, values max 512 chars. - - name: New name for the evaluation. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Eval: Updated evaluation object. - - Notes: - - Only name and metadata can be updated - - Cannot modify data_source_config or testing_criteria after creation - """ -``` - -### List Evaluations - -List all evaluations for the current project. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - order_by: Literal["created_at", "updated_at"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Eval]: - """ - List evaluations for a project. - - Args: - after: Cursor for pagination. ID of last eval from previous request. - - limit: Number of evaluations to retrieve. Default varies by API. - - order: Sort order by timestamp. - - "asc": Ascending (oldest first) - - "desc": Descending (newest first, default) - - order_by: Field to order by. - - "created_at": Creation time (default) - - "updated_at": Last modification time - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[Eval]: Paginated list of evaluations. - Supports iteration: for eval in client.evals.list(): ... - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# List all evaluations -for eval in client.evals.list(): - print(f"{eval.name}: {eval.id}") - -# List with pagination -page = client.evals.list(limit=10) -for eval in page: - print(eval.name) - -# Get next page -if page.has_more: - next_page = client.evals.list( - limit=10, - after=page.data[-1].id - ) - -# List by last updated -for eval in client.evals.list(order_by="updated_at", order="desc"): - print(f"{eval.name} - Updated: {eval.updated_at}") -``` - -### Delete Evaluation - -Delete an evaluation. - -```python { .api } -def delete( - self, - eval_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> EvalDeleteResponse: - """ - Delete an evaluation. - - Args: - eval_id: ID of the evaluation to delete. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - EvalDeleteResponse: Confirmation of deletion with ID and status. - - Notes: - - Deletion is permanent - - Associated eval runs are also deleted - """ -``` - -### Evaluation Runs - -Run an evaluation against a model configuration. - -```python { .api } -# Access via client.evals.runs - -def create( - self, - eval_id: str, - *, - data_source: dict, - metadata: dict[str, str] | None | Omit = omit, - name: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RunCreateResponse: - """ - Create a run of the evaluation with specified data source and configuration. - - Args: - eval_id: ID of the evaluation to run. - - data_source: Details about the run's data source. Can be either: - - File content: {"type": "file_content", "content": [...]} - - File ID: {"type": "file_id", "file_id": "file-xxx"} - The data source will be validated against the schema specified in the evaluation config. - - metadata: Set of 16 key-value pairs that can be attached to an object. - Keys have a maximum length of 64 characters. - Values have a maximum length of 512 characters. - - name: The name of the run. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - RunCreateResponse: Created run object. Use retrieve() to check status and results. - """ - -def retrieve( - self, - eval_id: str, - run_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> EvalRun: - """ - Retrieve a specific evaluation run. - - Args: - eval_id: ID of the evaluation. - run_id: ID of the run. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - EvalRun: Run object with status, results, and scores. - """ - -def cancel( - self, - run_id: str, - *, - eval_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RunCancelResponse: - """ - Cancel an ongoing evaluation run. - - Args: - run_id: ID of the run to cancel. - eval_id: ID of the evaluation. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - RunCancelResponse: Confirmation of run cancellation. - """ - -def delete( - self, - run_id: str, - *, - eval_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RunDeleteResponse: - """ - Permanently delete an evaluation run. - - Args: - run_id: ID of the run to delete. - eval_id: ID of the evaluation. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - RunDeleteResponse: Confirmation of run deletion. - """ - -def list( - self, - eval_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - status: Literal["queued", "in_progress", "completed", "canceled", "failed"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[RunListResponse]: - """ - List all runs for an evaluation. - - Args: - eval_id: ID of the evaluation. - after: Cursor for pagination. - limit: Number of runs to retrieve. - order: Sort order ("asc" or "desc"). - status: Filter by run status ("queued", "in_progress", "completed", "canceled", or "failed"). - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[RunListResponse]: Paginated list of runs. - """ -``` - -### Evaluation Run Output Items - -Inspect individual output items from an evaluation run. - -```python { .api } -# Access via client.evals.runs.output_items - -def retrieve( - self, - output_item_id: str, - *, - eval_id: str, - run_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> OutputItemRetrieveResponse: - """ - Get an evaluation run output item by ID. - - Args: - output_item_id: ID of the output item to retrieve. - - eval_id: ID of the evaluation. - - run_id: ID of the evaluation run. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - OutputItemRetrieveResponse: Individual output item with test results - and grader scores for a specific data point in the evaluation. - """ - -def list( - self, - run_id: str, - *, - eval_id: str, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - status: Literal["fail", "pass"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[OutputItemListResponse]: - """ - Get a list of output items for an evaluation run. - - Args: - run_id: ID of the evaluation run. - - eval_id: ID of the evaluation. - - after: Cursor for pagination. ID of last output item from previous request. - - limit: Number of output items to retrieve. - - order: Sort order by timestamp. - - "asc": Ascending (oldest first) - - "desc": Descending (newest first, default) - - status: Filter output items by status. - - "fail": Only failed output items - - "pass": Only passed output items - Omit to retrieve all output items. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[OutputItemListResponse]: Paginated list of output items. - Each output item contains the model output and grader results for - a single test case in the evaluation run. - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# List all output items for a run -for output_item in client.evals.runs.output_items.list( - eval_id="eval-abc123", - run_id="run-def456" -): - print(f"Item {output_item.id}: {output_item.status}") - if output_item.status == "fail": - print(f" Failed on: {output_item.grader_results}") - -# Filter only failed items -failed_items = client.evals.runs.output_items.list( - eval_id="eval-abc123", - run_id="run-def456", - status="fail" -) - -for item in failed_items: - print(f"Failed item: {item.id}") - # Retrieve detailed information - detail = client.evals.runs.output_items.retrieve( - eval_id="eval-abc123", - run_id="run-def456", - output_item_id=item.id - ) - print(f" Model output: {detail.model_output}") - print(f" Expected: {detail.expected}") - -# Paginate through all output items -page = client.evals.runs.output_items.list( - eval_id="eval-abc123", - run_id="run-def456", - limit=10, - order="asc" -) - -for item in page: - print(f"{item.id}: {item.status}") -``` - -Complete workflow example: - -```python -from openai import OpenAI -import time - -client = OpenAI() - -# 1. Create evaluation -eval = client.evals.create( - name="Model Comparison", - data_source_config={ - "type": "file", - "file_id": "file-abc123" - }, - testing_criteria=[ - { - "type": "exact_match", - "expected": "{{item.expected}}", - "actual": "{{sample.output_text}}" - } - ] -) - -# 2. Run evaluation with different models -run_gpt4 = client.evals.runs.create( - eval_id=eval.id, - model="gpt-4", - temperature=0.7, - metadata={"variant": "gpt-4"} -) - -run_gpt35 = client.evals.runs.create( - eval_id=eval.id, - model="gpt-3.5-turbo", - temperature=0.7, - metadata={"variant": "gpt-3.5"} -) - -# 3. Wait for completion and check results -def wait_for_run(eval_id: str, run_id: str): - while True: - run = client.evals.runs.retrieve(eval_id=eval_id, run_id=run_id) - if run.status in ["completed", "failed", "cancelled"]: - return run - time.sleep(2) - -gpt4_results = wait_for_run(eval.id, run_gpt4.id) -gpt35_results = wait_for_run(eval.id, run_gpt35.id) - -print(f"GPT-4 Score: {gpt4_results.score}") -print(f"GPT-3.5 Score: {gpt35_results.score}") - -# 4. Compare results -for run in client.evals.runs.list(eval_id=eval.id): - print(f"{run.model}: {run.score} ({run.status})") - -# 5. Update evaluation name -client.evals.update( - eval_id=eval.id, - name="Model Comparison - Updated", - metadata={"status": "active"} -) - -# 6. Clean up -client.evals.delete(eval_id=eval.id) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def run_eval(): - client = AsyncOpenAI() - - # Create evaluation - eval = await client.evals.create( - name="Async Eval", - data_source_config={"type": "file", "file_id": "file-abc"}, - testing_criteria=[{"type": "exact_match", "expected": "{{item.expected}}", "actual": "{{sample.output_text}}"}] - ) - - # Run evaluation - run = await client.evals.runs.create( - eval_id=eval.id, - model="gpt-4" - ) - - # Check results - result = await client.evals.runs.retrieve( - eval_id=eval.id, - run_id=run.id - ) - - return result.score - -score = asyncio.run(run_eval()) -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Eval(BaseModel): - """Evaluation object.""" - id: str - created_at: int - updated_at: int - name: str | None - object: Literal["eval"] - data_source_config: dict - testing_criteria: list[dict] - metadata: dict[str, str] | None - -class EvalRun(BaseModel): - """Evaluation run object.""" - id: str - eval_id: str - created_at: int - model: str - status: Literal["queued", "in_progress", "completed", "failed", "cancelled"] - score: float | None # Present when status="completed" - results: list[dict] | None # Detailed results per test case - metadata: dict[str, str] | None - -class EvalDeleteResponse(BaseModel): - """Response from delete operation.""" - id: str - deleted: bool - object: Literal["eval.deleted"] - -class OutputItemRetrieveResponse(BaseModel): - """Individual evaluation run output item.""" - id: str - eval_id: str - run_id: str - status: Literal["fail", "pass"] - model_output: str | dict # Model's generated output - expected: str | dict | None # Expected output if defined - grader_results: list[dict] # Results from each grader - created_at: int - -class OutputItemListResponse(BaseModel): - """Evaluation run output item in list responses.""" - id: str - eval_id: str - run_id: str - status: Literal["fail", "pass"] - created_at: int - -class SyncCursorPage[T]: - """Cursor-based pagination.""" - data: list[T] - has_more: bool - def __iter__(self) -> Iterator[T]: ... - -class Omit: - """Sentinel value for omitted parameters.""" -``` - -## Access Pattern - -```python -# Synchronous -from openai import OpenAI -client = OpenAI() -client.evals.create(...) -client.evals.retrieve(...) -client.evals.update(...) -client.evals.list(...) -client.evals.delete(...) -client.evals.runs.create(...) -client.evals.runs.retrieve(...) -client.evals.runs.list(...) -client.evals.runs.output_items.retrieve(...) -client.evals.runs.output_items.list(...) - -# Asynchronous -from openai import AsyncOpenAI -client = AsyncOpenAI() -await client.evals.create(...) -await client.evals.retrieve(...) -await client.evals.update(...) -await client.evals.list(...) -await client.evals.delete(...) -await client.evals.runs.create(...) -await client.evals.runs.retrieve(...) -await client.evals.runs.list(...) -await client.evals.runs.output_items.retrieve(...) -await client.evals.runs.output_items.list(...) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/files.md b/.tessl/tiles/tessl/pypi-openai/docs/files.md deleted file mode 100644 index aa2298c8e..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/files.md +++ /dev/null @@ -1,512 +0,0 @@ -# Files - -Upload and manage files for use with OpenAI features like Assistants, Fine-tuning, Batch processing, and Vision. Provides file storage with purpose-specific handling. - -## Capabilities - -### Upload File - -Upload a file for use with OpenAI services. - -```python { .api } -def create( - self, - *, - file: FileTypes, - purpose: FilePurpose, - expires_after: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FileObject: - """ - Upload a file for use with OpenAI services. - - Args: - file: File to upload. Can be file path string, file object, or tuple. - Maximum file size varies by purpose. - - purpose: Intended purpose of the file. Options: - - "assistants": For Assistants API and file_search tool - - "batch": For Batch API operations - - "fine-tune": For fine-tuning jobs - - "vision": For vision model inputs - - "user_data": Flexible file type for any purpose - - "evals": For evaluation data sets - - expires_after: Expiration policy for the file (ExpiresAfter type). By default, - files with purpose="batch" expire after 30 days and all other files persist - until manually deleted. Structure: - - anchor: "created_at" (file creation time) - - seconds: int (3600-2592000, time in seconds until expiration) - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FileObject: Uploaded file metadata including ID. - - Raises: - BadRequestError: Invalid file format, size, or purpose - AuthenticationError: Invalid API key - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Upload file for assistants -with open("document.pdf", "rb") as file: - response = client.files.create( - file=file, - purpose="assistants" - ) - -file_id = response.id -print(f"File ID: {file_id}") - -# Upload for fine-tuning -with open("training_data.jsonl", "rb") as file: - response = client.files.create( - file=file, - purpose="fine-tune" - ) - -# Upload for batch processing -with open("batch_requests.jsonl", "rb") as file: - response = client.files.create( - file=file, - purpose="batch" - ) - -# Using file_from_path helper -from openai import file_from_path - -response = client.files.create( - file=file_from_path("data.csv"), - purpose="assistants" -) - -# Check upload details -print(f"Filename: {response.filename}") -print(f"Size: {response.bytes} bytes") -print(f"Purpose: {response.purpose}") -print(f"Status: {response.status}") -``` - -### Retrieve File Metadata - -Get information about a specific file. - -```python { .api } -def retrieve( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FileObject: - """ - Retrieve file metadata. - - Args: - file_id: The ID of the file to retrieve. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FileObject: File metadata. - - Raises: - NotFoundError: File not found - """ -``` - -Usage example: - -```python -file = client.files.retrieve("file-abc123") - -print(f"Filename: {file.filename}") -print(f"Purpose: {file.purpose}") -print(f"Size: {file.bytes} bytes") -print(f"Created: {file.created_at}") -``` - -### List Files - -List all uploaded files with optional filtering. - -```python { .api } -def list( - self, - *, - purpose: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - after: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[FileObject]: - """ - List uploaded files with optional filtering and pagination. - - Args: - purpose: Filter by file purpose (e.g., "assistants", "fine-tune"). - limit: Number of files to retrieve (max 10000). Default 10000. - order: Sort order. "asc" for ascending, "desc" for descending. Default "desc". - after: Cursor for pagination. Return files after this file ID. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[FileObject]: Cursor-paginated list of files. - """ -``` - -Usage examples: - -```python -# List all files -files = client.files.list() - -for file in files.data: - print(f"{file.filename} ({file.id})") - -# Filter by purpose -assistant_files = client.files.list(purpose="assistants") - -# Pagination -page1 = client.files.list(limit=10) -page2 = client.files.list(limit=10, after=page1.data[-1].id) - -# Iterate through all files -for file in client.files.list(): - print(file.filename) -``` - -### Delete File - -Delete a file from OpenAI storage. - -```python { .api } -def delete( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FileDeleted: - """ - Delete a file. - - Args: - file_id: The ID of the file to delete. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FileDeleted: Deletion confirmation. - - Raises: - NotFoundError: File not found - """ -``` - -Usage example: - -```python -# Delete file -result = client.files.delete("file-abc123") - -print(f"Deleted: {result.id}") -print(f"Success: {result.deleted}") -``` - -### Download File Content - -Retrieve the binary content of a file. - -```python { .api } -def content( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> HttpxBinaryResponseContent: - """ - Retrieve file content. - - Args: - file_id: The ID of the file to download. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - HttpxBinaryResponseContent: File content as binary data. - - Raises: - NotFoundError: File not found - """ -``` - -Usage example: - -```python -from pathlib import Path - -# Download file content -content = client.files.content("file-abc123") - -# Save to file -Path("downloaded_file.txt").write_bytes(content.content) - -# Or use read() -file_bytes = content.read() - -# Stream to file -content.stream_to_file("output.txt") -``` - -### Retrieve File Content (Alias) - -Retrieve the textual content of a file. This is an alias method that retrieves content and returns it as a string. - -```python { .api } -def retrieve_content( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> str: - """ - Retrieve file content as a string. - - This method retrieves the contents of the specified file and returns - it as a string. For binary content, use the content() method instead. - - Args: - file_id: The ID of the file to retrieve content from. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - str: The file content as a string. - - Raises: - NotFoundError: File not found - """ -``` - -Usage example: - -```python -# Retrieve file content as string -text_content = client.files.retrieve_content("file-abc123") -print(text_content) -``` - -### Wait for Processing - -Poll until file processing is complete (helper method). - -```python { .api } -def wait_for_processing( - self, - file_id: str, - *, - poll_interval: float = 5.0, - max_wait_seconds: float = 1800, -) -> FileObject: - """ - Wait for file processing to complete. - - Args: - file_id: The ID of the file to wait for. - poll_interval: Seconds between status checks. Default 5.0. - max_wait_seconds: Maximum seconds to wait. Default 1800 (30 minutes). - - Returns: - FileObject: File with completed status. - - Raises: - TimeoutError: Processing not completed within max_wait_seconds - APIError: Processing failed - """ -``` - -Usage example: - -```python -# Upload and wait -with open("large_file.pdf", "rb") as file: - uploaded = client.files.create(file=file, purpose="assistants") - -# Wait for processing -ready_file = client.files.wait_for_processing(uploaded.id) - -print(f"File ready: {ready_file.status}") -``` - -## Types - -```python { .api } -from typing import Literal, TypedDict, Required, Union, Iterator -from pydantic import BaseModel - -class FileObject(BaseModel): - """File metadata.""" - id: str - bytes: int - created_at: int - filename: str - object: Literal["file"] - purpose: FilePurpose - status: FileStatus - status_details: str | None - -class FileDeleted(BaseModel): - """File deletion confirmation.""" - id: str - deleted: bool - object: Literal["file"] - -FilePurpose = Literal[ - "assistants", - "assistants_output", - "batch", - "batch_output", - "fine-tune", - "fine-tune-results", - "vision", - "user_data", - "evals" -] - -FileStatus = Literal["uploaded", "processed", "error"] - -class ExpiresAfter(TypedDict): - """File expiration policy configuration.""" - anchor: Required[Literal["created_at"]] - """Anchor timestamp after which the expiration policy applies. Currently only 'created_at' is supported.""" - - seconds: Required[int] - """Number of seconds after the anchor time that the file will expire. Must be between 3600 (1 hour) and 2592000 (30 days).""" - -# File types -FileTypes = Union[ - FileContent, # File-like object - tuple[str | None, FileContent], # (filename, content) - tuple[str | None, FileContent, str | None] # (filename, content, content_type) -] - -# Pagination -class SyncPage[T](BaseModel): - data: list[T] - object: str - has_more: bool - def __iter__(self) -> Iterator[T]: ... -``` - -## File Size Limits - -| Purpose | Format | Max Size | -|---------|--------|----------| -| assistants | Various | 512 MB | -| batch | JSONL | 100 MB | -| fine-tune | JSONL | 1 GB | -| vision | Images | 20 MB | - -## Best Practices - -```python -from openai import OpenAI -from pathlib import Path - -client = OpenAI() - -# 1. Check file exists before upload -file_path = Path("data.txt") -if file_path.exists(): - with open(file_path, "rb") as f: - file = client.files.create(file=f, purpose="assistants") - -# 2. Clean up unused files -files = client.files.list(purpose="assistants") -for file in files: - if should_delete(file): - client.files.delete(file.id) - -# 3. Handle upload errors -from openai import APIError - -try: - with open("large_file.pdf", "rb") as f: - file = client.files.create(file=f, purpose="assistants") -except APIError as e: - print(f"Upload failed: {e}") - -# 4. Track file IDs for later use -uploaded_files = [] - -for file_path in ["file1.txt", "file2.txt"]: - with open(file_path, "rb") as f: - file = client.files.create(file=f, purpose="assistants") - uploaded_files.append(file.id) - -# Use files with assistant -assistant = client.beta.assistants.create( - model="gpt-4", - tools=[{"type": "file_search"}], - tool_resources={"file_search": {"file_ids": uploaded_files}} -) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def upload_file(): - client = AsyncOpenAI() - - with open("document.pdf", "rb") as file: - response = await client.files.create( - file=file, - purpose="assistants" - ) - - return response.id - -file_id = asyncio.run(upload_file()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md b/.tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md deleted file mode 100644 index 776da1e0f..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/fine-tuning.md +++ /dev/null @@ -1,845 +0,0 @@ -# Fine-tuning - -Create and manage fine-tuning jobs to customize models on your own data. Fine-tune base models to improve performance on specific tasks, add domain knowledge, or adjust model behavior. - -## Capabilities - -### Create Fine-tuning Job - -Start a fine-tuning job to train a custom model. - -```python { .api } -def create( - self, - *, - model: str, - training_file: str, - hyperparameters: dict | Omit = omit, - method: dict | Omit = omit, - integrations: list[dict] | Omit = omit, - metadata: dict[str, str] | Omit = omit, - seed: int | Omit = omit, - suffix: str | Omit = omit, - validation_file: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FineTuningJob: - """ - Create a fine-tuning job to train a custom model. - - Args: - model: Base model to fine-tune. Supported models: - - "gpt-4o-2024-08-06" - - "gpt-4o-mini-2024-07-18" - - "gpt-3.5-turbo" - - "babbage-002", "davinci-002" - - training_file: ID of uploaded training file (JSONL format). - Each line: {"messages": [...], "response": ...} - - hyperparameters: Training hyperparameters. Options: - - n_epochs: Number of training epochs (auto or 1-50) - - batch_size: Batch size (auto or specific value) - - learning_rate_multiplier: Learning rate multiplier (auto or 0.02-2) - - method: Fine-tuning method configuration. Options: - - {"type": "supervised"}: Standard supervised fine-tuning - - {"type": "dpo"}: Direct Preference Optimization - - {"type": "reinforcement"}: Reinforcement learning - - integrations: External tool integrations. Options: - - Weights & Biases: [{"type": "wandb", "wandb": {"project": "..."}}] - - metadata: Key-value pairs for storing additional information (max 16 pairs). - Keys max 64 chars, values max 512 chars. - - seed: Random seed for reproducibility. - - suffix: Custom suffix for fine-tuned model name (max 18 chars). - Final model name: ft:{base_model}:{suffix}:{job_id} - - validation_file: ID of validation file for evaluation during training. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FineTuningJob: Created job with status "created" or "validating_files". - - Raises: - BadRequestError: Invalid parameters or file format - NotFoundError: Training file not found - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Upload training data first -with open("training_data.jsonl", "rb") as f: - training_file = client.files.create(file=f, purpose="fine-tune") - -# Create fine-tuning job -job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file=training_file.id -) - -print(f"Job ID: {job.id}") -print(f"Status: {job.status}") - -# With hyperparameters -job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file=training_file.id, - hyperparameters={ - "n_epochs": 3, - "batch_size": 4, - "learning_rate_multiplier": 0.1 - } -) - -# With validation file -with open("validation_data.jsonl", "rb") as f: - validation_file = client.files.create(file=f, purpose="fine-tune") - -job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file=training_file.id, - validation_file=validation_file.id, - suffix="my-custom-model" -) - -# With Weights & Biases integration -job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file=training_file.id, - integrations=[{ - "type": "wandb", - "wandb": { - "project": "my-fine-tuning", - "name": "experiment-1" - } - }] -) - -# Using DPO method -job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file=training_file.id, - method={"type": "dpo"} -) -``` - -### Retrieve Fine-tuning Job - -Get status and details of a fine-tuning job. - -```python { .api } -def retrieve( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FineTuningJob: - """ - Retrieve fine-tuning job details. - - Args: - fine_tuning_job_id: The ID of the fine-tuning job. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FineTuningJob: Job details including current status. - - Raises: - NotFoundError: Job not found - """ -``` - -Usage example: - -```python -# Check job status -job = client.fine_tuning.jobs.retrieve("ftjob-abc123") - -print(f"Status: {job.status}") -print(f"Model: {job.fine_tuned_model}") -print(f"Trained tokens: {job.trained_tokens}") - -if job.status == "succeeded": - print(f"Fine-tuned model ready: {job.fine_tuned_model}") -elif job.status == "failed": - print(f"Error: {job.error}") -``` - -### List Fine-tuning Jobs - -List fine-tuning jobs with optional filtering and pagination. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - metadata: dict[str, str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[FineTuningJob]: - """ - List fine-tuning jobs with pagination. - - Args: - after: Cursor for pagination. Return jobs after this job ID. - limit: Number of jobs to retrieve (max 100). Default 20. - metadata: Optional metadata filter. To filter, use the syntax `metadata[k]=v`. - Alternatively, set `metadata=null` to indicate no metadata. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[FineTuningJob]: Paginated list of jobs. - """ -``` - -Usage example: - -```python -# List all jobs -jobs = client.fine_tuning.jobs.list() - -for job in jobs: - print(f"{job.id}: {job.status}") - -# Pagination -page1 = client.fine_tuning.jobs.list(limit=10) -page2 = client.fine_tuning.jobs.list(limit=10, after=page1.data[-1].id) - -# Filter by status -succeeded_jobs = [ - job for job in client.fine_tuning.jobs.list() - if job.status == "succeeded" -] -``` - -### Cancel Fine-tuning Job - -Cancel an in-progress fine-tuning job. - -```python { .api } -def cancel( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FineTuningJob: - """ - Cancel a running fine-tuning job. - - Args: - fine_tuning_job_id: The ID of the job to cancel. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FineTuningJob: Job with status "cancelled". - - Raises: - NotFoundError: Job not found - BadRequestError: Job not in cancellable state - """ -``` - -Usage example: - -```python -# Cancel job -job = client.fine_tuning.jobs.cancel("ftjob-abc123") - -print(f"Status: {job.status}") # "cancelled" -``` - -### Pause Fine-tuning Job - -Temporarily pause a running fine-tuning job to save costs. - -```python { .api } -def pause( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FineTuningJob: - """ - Pause a running fine-tuning job. - - Args: - fine_tuning_job_id: The ID of the job to pause. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FineTuningJob: Job with status "paused". - - Raises: - NotFoundError: Job not found - BadRequestError: Job not in pauseable state - """ -``` - -### Resume Fine-tuning Job - -Resume a previously paused fine-tuning job. - -```python { .api } -def resume( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> FineTuningJob: - """ - Resume a paused fine-tuning job. - - Args: - fine_tuning_job_id: The ID of the job to resume. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - FineTuningJob: Job with status "running". - - Raises: - NotFoundError: Job not found - BadRequestError: Job not in resumeable state - """ -``` - -Usage example: - -```python -# Pause job to save costs -job = client.fine_tuning.jobs.pause("ftjob-abc123") -print(f"Status: {job.status}") # "paused" - -# Resume later -job = client.fine_tuning.jobs.resume("ftjob-abc123") -print(f"Status: {job.status}") # "running" -``` - -### List Job Events - -Stream events and training metrics from a fine-tuning job. - -```python { .api } -def list_events( - self, - fine_tuning_job_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[FineTuningJobEvent]: - """ - List events for a fine-tuning job (training logs, metrics). - - Args: - fine_tuning_job_id: The job ID. - after: Return events after this event ID. - limit: Number of events to retrieve (max 100). Default 20. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[FineTuningJobEvent]: Job events and logs. - """ -``` - -Usage example: - -```python -# Get training events -events = client.fine_tuning.jobs.list_events("ftjob-abc123") - -for event in events: - print(f"[{event.created_at}] {event.message}") - if event.data: - print(f" Metrics: {event.data}") - -# Monitor training progress -import time - -job_id = "ftjob-abc123" -while True: - job = client.fine_tuning.jobs.retrieve(job_id) - - if job.status in ["succeeded", "failed", "cancelled"]: - break - - events = client.fine_tuning.jobs.list_events(job_id, limit=5) - for event in events: - print(event.message) - - time.sleep(10) -``` - -### List Checkpoints - -List checkpoints saved during fine-tuning. - -```python { .api } -def list( - self, - fine_tuning_job_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[FineTuningJobCheckpoint]: - """ - List checkpoints for a fine-tuning job. - - Args: - fine_tuning_job_id: The job ID. - after: Return checkpoints after this checkpoint ID. - limit: Number of checkpoints to retrieve. Default 10. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[FineTuningJobCheckpoint]: Job checkpoints. - """ -``` - -Usage example: - -```python -# List checkpoints -checkpoints = client.fine_tuning.jobs.checkpoints.list("ftjob-abc123") - -for checkpoint in checkpoints: - print(f"Step {checkpoint.step_number}: {checkpoint.metrics}") - print(f" Model: {checkpoint.fine_tuned_model_checkpoint}") -``` - -### Checkpoint Permissions - -Manage access permissions for fine-tuning checkpoints to enable sharing. - -```python { .api } -# Accessed via: client.fine_tuning.checkpoints.permissions -def create( - self, - fine_tuned_model_checkpoint: str, - *, - project_ids: list[str], - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncPage[PermissionCreateResponse]: - """ - Grant access permission to a checkpoint. - - NOTE: Calling this endpoint requires an admin API key. - This enables organization owners to share fine-tuned models with other projects - in their organization. - - Args: - fine_tuned_model_checkpoint: The checkpoint model ID (e.g., "ftckpt-xyz789"). - project_ids: The project identifiers to grant access to (list of strings). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncPage[PermissionCreateResponse]: Paginated list of created permissions. - """ - -def retrieve( - self, - fine_tuned_model_checkpoint: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["ascending", "descending"] | Omit = omit, - project_id: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> PermissionRetrieveResponse: - """ - List all permissions for a checkpoint. - - NOTE: This endpoint requires an admin API key. - Organization owners can use this endpoint to view all permissions for a - fine-tuned model checkpoint. - - Args: - fine_tuned_model_checkpoint: The checkpoint model ID. - after: Identifier for the last permission ID from the previous pagination request. - limit: Number of permissions to retrieve. - order: The order in which to retrieve permissions ("ascending" or "descending"). - project_id: The ID of the project to get permissions for (filter). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - PermissionRetrieveResponse: Permissions list response. - """ - -def delete( - self, - permission_id: str, - *, - fine_tuned_model_checkpoint: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> PermissionDeleteResponse: - """ - Revoke a checkpoint permission. - - NOTE: This endpoint requires an admin API key. - Organization owners can use this endpoint to delete a permission for a - fine-tuned model checkpoint. - - Args: - permission_id: The ID of the permission to delete (positional parameter). - fine_tuned_model_checkpoint: The checkpoint model ID (keyword-only parameter). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - PermissionDeleteResponse: Deletion confirmation. - """ -``` - -Usage example: - -```python -# Grant checkpoint access to other projects -permissions = client.fine_tuning.checkpoints.permissions.create( - fine_tuned_model_checkpoint="ftckpt-xyz789", - project_ids=["proj-456", "proj-789"] -) - -print(f"Created {len(permissions.data)} permissions") -for perm in permissions: - print(f" Permission ID: {perm.id}") - -# Retrieve all permissions for a checkpoint -all_perms = client.fine_tuning.checkpoints.permissions.retrieve( - fine_tuned_model_checkpoint="ftckpt-xyz789" -) - -print(f"Total permissions: {len(all_perms.data)}") -for perm in all_perms.data: - print(f" {perm.id}: Project {perm.project_id}") - -# Filter permissions by project -project_perms = client.fine_tuning.checkpoints.permissions.retrieve( - fine_tuned_model_checkpoint="ftckpt-xyz789", - project_id="proj-456" -) - -# Revoke a permission -deleted = client.fine_tuning.checkpoints.permissions.delete( - permission_id="perm-abc123", - fine_tuned_model_checkpoint="ftckpt-xyz789" -) - -print(f"Deleted: {deleted.deleted}") # True -``` - -### Alpha Graders - -Run graders to evaluate fine-tuning results (alpha feature). - -```python { .api } -# Accessed via: client.fine_tuning.alpha.graders -def run( - self, - *, - grader: dict, - model_sample: str, - item: object | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> GraderRunResponse: - """ - Run a grader on a model sample. - - Args: - grader: The grader used for the fine-tuning job (structured Grader object). - model_sample: The model sample to be evaluated. This value will be used to - populate the `sample` namespace. See the guide for more details. - The `output_json` variable will be populated if the model sample is - a valid JSON string. - item: The dataset item provided to the grader. This will be used to - populate the `item` namespace. See the guide for more details. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - GraderRunResponse: Grading results with scores and analysis. - """ - -def validate( - self, - *, - grader: dict, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> GraderValidateResponse: - """ - Validate a grader configuration before running. - - Args: - grader: Grader configuration to validate (structured Grader object). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ValidationResult: Validation status and any errors. - """ -``` - -Usage example: - -```python -# Validate grader configuration -validation = client.fine_tuning.alpha.graders.validate( - grader_config={ - "type": "model_grader", - "criteria": "accuracy", - "model": "gpt-4" - } -) - -if validation.valid: - # Run grader on evaluation data - result = client.fine_tuning.alpha.graders.run( - grader_config={ - "type": "model_grader", - "criteria": "accuracy" - }, - evaluation_data=[ - { - "input": "What is 2+2?", - "expected": "4", - "actual": "4" - } - ], - model="ft:gpt-3.5-turbo:my-org:custom_suffix:id" - ) - - print(f"Score: {result.score}") - print(f"Analysis: {result.analysis}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class FineTuningJob(BaseModel): - """Fine-tuning job.""" - id: str - created_at: int - error: dict | None - fine_tuned_model: str | None - finished_at: int | None - hyperparameters: dict - model: str - object: Literal["fine_tuning.job"] - organization_id: str - result_files: list[str] - seed: int - status: Literal[ - "validating_files", "queued", "running", - "succeeded", "failed", "cancelled" - ] - trained_tokens: int | None - training_file: str - validation_file: str | None - integrations: list[dict] | None - user_provided_suffix: str | None - -class FineTuningJobEvent(BaseModel): - """Job event/log entry.""" - id: str - created_at: int - level: Literal["info", "warn", "error"] - message: str - object: Literal["fine_tuning.job.event"] - data: dict | None - type: str - -class FineTuningJobCheckpoint(BaseModel): - """Training checkpoint.""" - id: str - created_at: int - fine_tuned_model_checkpoint: str - fine_tuning_job_id: str - metrics: dict - object: Literal["fine_tuning.job.checkpoint"] - step_number: int - -# Pagination -class SyncCursorPage[T](BaseModel): - data: list[T] - object: str - has_more: bool - def __iter__(self) -> Iterator[T]: ... -``` - -## Training Data Format - -Training data must be JSONL (JSON Lines) format: - -```jsonl -{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 2+2?"}, {"role": "assistant", "content": "4"}]} -{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the capital of France?"}, {"role": "assistant", "content": "Paris"}]} -``` - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Validate training data before upload -def validate_jsonl(file_path: str) -> bool: - import json - - with open(file_path) as f: - for i, line in enumerate(f): - try: - data = json.loads(line) - assert "messages" in data - except Exception as e: - print(f"Line {i}: {e}") - return False - return True - -# 2. Monitor job progress -def wait_for_job(job_id: str): - import time - - while True: - job = client.fine_tuning.jobs.retrieve(job_id) - - if job.status == "succeeded": - return job.fine_tuned_model - elif job.status in ["failed", "cancelled"]: - raise Exception(f"Job {job.status}: {job.error}") - - print(f"Status: {job.status}") - time.sleep(30) - -# 3. Use validation file for monitoring -with open("train.jsonl", "rb") as train_f, \ - open("val.jsonl", "rb") as val_f: - - train_file = client.files.create(file=train_f, purpose="fine-tune") - val_file = client.files.create(file=val_f, purpose="fine-tune") - - job = client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file=train_file.id, - validation_file=val_file.id - ) - -# 4. Clean up after completion -fine_tuned_model = wait_for_job(job.id) - -# Use the model -response = client.chat.completions.create( - model=fine_tuned_model, - messages=[{"role": "user", "content": "Test"}] -) - -# Later, delete if no longer needed -client.models.delete(fine_tuned_model) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_fine_tune(): - client = AsyncOpenAI() - - job = await client.fine_tuning.jobs.create( - model="gpt-3.5-turbo", - training_file="file-abc123" - ) - - return job.id - -job_id = asyncio.run(create_fine_tune()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/images.md b/.tessl/tiles/tessl/pypi-openai/docs/images.md deleted file mode 100644 index aee44b411..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/images.md +++ /dev/null @@ -1,617 +0,0 @@ -# Images - -Generate, edit, and create variations of images using DALL-E models. Supports creating images from text prompts, editing existing images with masks, and generating variations. - -## Capabilities - -### Generate Images - -Create images from text descriptions using DALL-E or GPT-Image-1 models. - -```python { .api } -def generate( - self, - *, - prompt: str, - background: Literal["transparent", "opaque", "auto"] | Omit = omit, - model: str | ImageModel | Omit = omit, - moderation: Literal["low", "auto"] | Omit = omit, - n: int | Omit = omit, - output_compression: int | Omit = omit, - output_format: Literal["png", "jpeg", "webp"] | Omit = omit, - partial_images: int | Omit = omit, - quality: Literal["standard", "hd", "low", "medium", "high", "auto"] | Omit = omit, - response_format: Literal["url", "b64_json"] | Omit = omit, - size: Literal["auto", "1024x1024", "1536x1024", "1024x1536", "256x256", "512x512", "1792x1024", "1024x1792"] | Omit = omit, - stream: bool | Omit = omit, - style: Literal["vivid", "natural"] | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ImagesResponse | Stream[ImageGenStreamEvent]: - """ - Generate images from text prompts. - - Args: - prompt: Text description of the desired image(s). Max length: - - gpt-image-1: 32000 characters - - dall-e-3: 4000 characters - - dall-e-2: 1000 characters - - background: (gpt-image-1 only) Background transparency control. Options: - - "transparent": Transparent background (requires png or webp output) - - "opaque": Opaque background - - "auto": Model determines best option (default) - - model: Model to use. Options: - - "dall-e-3": Latest DALL-E, highest quality - - "dall-e-2": Previous DALL-E generation - - "gpt-image-1": Latest generation model with advanced features - Defaults to dall-e-2 unless gpt-image-1 specific parameters are used. - - moderation: (gpt-image-1 only) Content moderation level. Options: - - "auto": Standard filtering (default) - - "low": Less restrictive filtering - - n: Number of images to generate. Default 1. - - gpt-image-1: Supports 1-10 - - dall-e-3: Only supports n=1 - - dall-e-2: Supports 1-10 - - output_compression: (gpt-image-1 only) Compression level (0-100%) for webp or jpeg - output formats. Default 100. Higher values = better quality, larger file size. - - output_format: (gpt-image-1 only) Output image format. Options: - - "png": PNG format (default), supports transparency - - "jpeg": JPEG format, smaller files, no transparency - - "webp": WebP format, good compression with quality - - partial_images: (gpt-image-1 only) Number of partial images for streaming (0-3). - Set to 0 for single final image. Default returns progressive partial images. - Final image may arrive before all partials if generation completes quickly. - - quality: Image quality. Options depend on model: - - gpt-image-1: "auto" (default), "high", "medium", "low" - - dall-e-3: "standard" (default), "hd" - - dall-e-2: Only "standard" - - response_format: Response data format. Options: - - "url": Returns URL (default), expires after 1 hour (dall-e-2 and dall-e-3 only) - - "b64_json": Returns base64-encoded JSON - Note: gpt-image-1 always returns base64-encoded images. - - size: Image dimensions. Options depend on model: - - gpt-image-1: "auto" (default), "1024x1024", "1536x1024", "1024x1536" - - dall-e-3: "1024x1024", "1792x1024", "1024x1792" - - dall-e-2: "256x256", "512x512", "1024x1024" - - stream: (gpt-image-1 only) If true, returns Stream[ImageGenStreamEvent] for - progressive image generation. Set partial_images to control streaming behavior. - - style: Visual style. Only for dall-e-3. Options: - - "vivid": Hyper-real and dramatic (default) - - "natural": More natural, less hyper-real - - user: Unique end-user identifier for abuse monitoring. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ImagesResponse: Contains generated image(s) when stream=False. - Stream[ImageGenStreamEvent]: Streaming events when stream=True (gpt-image-1 only). - - Raises: - BadRequestError: Invalid parameters or prompt violates content policy - RateLimitError: Rate limit exceeded - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic image generation -response = client.images.generate( - model="dall-e-3", - prompt="A cute baby sea otter wearing a beret", - n=1, - size="1024x1024" -) - -image_url = response.data[0].url -print(f"Image URL: {image_url}") - -# HD quality -response = client.images.generate( - model="dall-e-3", - prompt="A serene mountain landscape at sunset", - quality="hd", - size="1792x1024" -) - -# Natural style -response = client.images.generate( - model="dall-e-3", - prompt="A portrait of a programmer at work", - style="natural" -) - -# Base64 response (no expiration) -response = client.images.generate( - model="dall-e-3", - prompt="A futuristic cityscape", - response_format="b64_json" -) - -import base64 -from pathlib import Path - -# Save base64 image -image_data = base64.b64decode(response.data[0].b64_json) -Path("generated_image.png").write_bytes(image_data) - -# Download from URL -import requests - -response = client.images.generate( - model="dall-e-3", - prompt="A cat playing chess" -) - -image_url = response.data[0].url -image_response = requests.get(image_url) -Path("cat_chess.png").write_bytes(image_response.content) - -# Multiple images with DALL-E 2 -response = client.images.generate( - model="dall-e-2", - prompt="Abstract art with geometric shapes", - n=4, - size="512x512" -) - -for i, image in enumerate(response.data): - print(f"Image {i+1}: {image.url}") - -# Check revised prompt (DALL-E 3 may modify prompts) -response = client.images.generate( - model="dall-e-3", - prompt="A dog" -) - -print(f"Original prompt: A dog") -print(f"Revised prompt: {response.data[0].revised_prompt}") -``` - -### Edit Images - -Modify an existing image using a mask and text prompt. Supports both DALL-E 2 and GPT-Image-1 models. - -```python { .api } -def edit( - self, - *, - image: FileTypes | list[FileTypes], - prompt: str, - background: Literal["transparent", "opaque", "auto"] | Omit = omit, - input_fidelity: Literal["high", "low"] | Omit = omit, - mask: FileTypes | Omit = omit, - model: str | ImageModel | Omit = omit, - n: int | Omit = omit, - output_compression: int | Omit = omit, - output_format: Literal["png", "jpeg", "webp"] | Omit = omit, - partial_images: int | Omit = omit, - quality: Literal["standard", "low", "medium", "high", "auto"] | Omit = omit, - response_format: Literal["url", "b64_json"] | Omit = omit, - size: Literal["256x256", "512x512", "1024x1024", "1536x1024", "1024x1536", "auto"] | Omit = omit, - stream: bool | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ImagesResponse | Stream[ImageEditStreamEvent]: - """ - Edit or extend an image using a mask and prompt. Supports dall-e-2 and gpt-image-1. - - Args: - image: Image(s) to edit. - - dall-e-2: Single PNG image, must be square, less than 4MB - - gpt-image-1: Single image or list of up to 16 images (png/webp/jpg), less than 50MB each - Transparent areas indicate where to edit if no mask provided. - - prompt: Text description of desired edits. - - dall-e-2: Max 1000 characters - - gpt-image-1: Max 32000 characters - - background: (gpt-image-1 only) Background transparency control. Options: - - "transparent": Transparent background (requires png or webp output) - - "opaque": Opaque background - - "auto": Model determines best option (default) - - input_fidelity: (gpt-image-1 only, not supported for gpt-image-1-mini) Control how much - the model matches input image style and features, especially facial features. Options: - - "low": Less strict matching (default) - - "high": Stricter matching of input features - - mask: Optional mask image. Must be PNG, same dimensions as image, less than 4MB. - Fully transparent areas (alpha=0) indicate where to edit. If multiple input images - are provided, mask applies to the first image. - - model: Model to use. Options: - - "dall-e-2": Original model, limited features - - "gpt-image-1": Latest model with advanced features (default if gpt-image-1 - specific parameters are used) - - n: Number of variations to generate (1-10). Default 1. - - output_compression: (gpt-image-1 only) Compression level (0-100%) for webp or jpeg - output formats. Default 100. Higher values = better quality, larger file size. - - output_format: (gpt-image-1 only) Output image format. Options: - - "png": PNG format (default), supports transparency - - "jpeg": JPEG format, smaller files, no transparency - - "webp": WebP format, good compression with quality - - partial_images: (gpt-image-1 only) Number of partial images for streaming (0-3). - Set to 0 for single final image. Default returns progressive partial images. - Final image may arrive before all partials if generation completes quickly. - - quality: (gpt-image-1 only) Output quality level. Options: - - "standard": Standard quality - - "low": Lower quality, faster - - "medium": Medium quality - - "high": High quality, slower - - "auto": Model determines best quality - - response_format: Response data format. Options: - - "url": Returns URL (default), expires after 1 hour - - "b64_json": Returns base64-encoded JSON - - size: Output dimensions. Options: - - dall-e-2: "256x256", "512x512", "1024x1024" (default) - - gpt-image-1: Same as dall-e-2 plus "1536x1024", "1024x1536", "auto" - - stream: (gpt-image-1 only) If true, returns Stream[ImageEditStreamEvent] for - progressive image generation. Set partial_images to control streaming behavior. - - user: Unique end-user identifier for abuse monitoring. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ImagesResponse: Contains edited image(s) when stream=False. - Stream[ImageEditStreamEvent]: Streaming events when stream=True (gpt-image-1 only). - - Raises: - BadRequestError: Invalid image format, size, or prompt - """ -``` - -Usage examples: - -```python -from openai import OpenAI -from pathlib import Path - -client = OpenAI() - -# Basic edit with DALL-E 2 -with open("base_image.png", "rb") as image_file, \ - open("mask.png", "rb") as mask_file: - - response = client.images.edit( - image=image_file, - mask=mask_file, - prompt="Add a red hat", - model="dall-e-2", - n=1, - size="1024x1024" - ) - -# Save result -import requests -image_url = response.data[0].url -image_data = requests.get(image_url).content -Path("edited_image.png").write_bytes(image_data) - -# Edit with GPT-Image-1 - multiple images -with open("image1.png", "rb") as img1, \ - open("image2.png", "rb") as img2: - - response = client.images.edit( - image=[img1, img2], # Multiple images for context - prompt="Blend these images together with a sunset background", - model="gpt-image-1", - output_format="webp", - quality="high", - background="auto" - ) - -# GPT-Image-1 with high input fidelity -with open("portrait.png", "rb") as image_file: - response = client.images.edit( - image=image_file, - prompt="Change the background to a beach scene, keep facial features", - model="gpt-image-1", - input_fidelity="high", # Preserve facial features - output_format="png", - size="1536x1024" - ) - -# GPT-Image-1 with transparent background -with open("object.png", "rb") as image_file: - response = client.images.edit( - image=image_file, - prompt="Remove the background", - model="gpt-image-1", - background="transparent", - output_format="png" # Must use png or webp for transparency - ) - -# Streaming edit with GPT-Image-1 -with open("base_image.png", "rb") as image_file: - stream = client.images.edit( - image=image_file, - prompt="Add dramatic lighting", - model="gpt-image-1", - stream=True, - partial_images=3 # Get 3 progressive partial images - ) - - for event in stream: - if event.type == "image": - print(f"Received image chunk: {len(event.data)} bytes") - elif event.type == "done": - print("Final image complete") - -# Compressed output for web use -with open("large_image.png", "rb") as image_file: - response = client.images.edit( - image=image_file, - prompt="Enhance colors", - model="gpt-image-1", - output_format="webp", - output_compression=85, # 85% quality for smaller file size - quality="medium" - ) - -# Edit using transparency (no mask) - works with both models -with open("image_with_transparency.png", "rb") as image_file: - response = client.images.edit( - image=image_file, - prompt="Fill transparent areas with flowers" - ) -``` - -### Create Variations - -Generate variations of an existing image (DALL-E 2 only). - -```python { .api } -def create_variation( - self, - *, - image: FileTypes, - model: str | ImageModel | Omit = omit, - n: int | Omit = omit, - response_format: Literal["url", "b64_json"] | Omit = omit, - size: Literal["256x256", "512x512", "1024x1024"] | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ImagesResponse: - """ - Create variations of an existing image. Only supports dall-e-2. - - Args: - image: Base image for variations. Must be valid PNG, less than 4MB, and square. - - model: Model to use. Only "dall-e-2" is supported for variations. - - n: Number of variations to generate (1-10). Default 1. - - response_format: Output format. - - "url": Returns URL (default), expires after 1 hour - - "b64_json": Returns base64-encoded JSON - - size: Output dimensions. Options: "256x256", "512x512", "1024x1024". - Default "1024x1024". - - user: Unique end-user identifier. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ImagesResponse: Contains variation image(s). - - Raises: - BadRequestError: Invalid image format or size - """ -``` - -Usage example: - -```python -from openai import OpenAI -import requests -from pathlib import Path - -client = OpenAI() - -# Create multiple variations -with open("base_image.png", "rb") as image_file: - response = client.images.create_variation( - image=image_file, - n=3, - size="1024x1024" - ) - -# Save all variations -for i, image in enumerate(response.data): - image_data = requests.get(image.url).content - Path(f"variation_{i+1}.png").write_bytes(image_data) - -# Base64 format -with open("base_image.png", "rb") as image_file: - response = client.images.create_variation( - image=image_file, - response_format="b64_json" - ) - -import base64 -image_data = base64.b64decode(response.data[0].b64_json) -Path("variation.png").write_bytes(image_data) -``` - -## Types - -```python { .api } -from typing import Literal, Union -from pydantic import BaseModel - -class ImagesResponse(BaseModel): - """Response from image endpoints.""" - created: int - data: list[Image] - -class Image(BaseModel): - """Single generated/edited image.""" - url: str | None # Present when response_format="url" - b64_json: str | None # Present when response_format="b64_json" - revised_prompt: str | None # Only for dall-e-3 generations - -class ImageEditStreamEvent(BaseModel): - """Streaming event from images.edit() with stream=True (gpt-image-1 only).""" - type: Literal["image", "done", "error"] - data: bytes | None # Image data for type="image" - error: str | None # Error message for type="error" - -class ImageGenStreamEvent(BaseModel): - """Streaming event from images.generate() with stream=True (gpt-image-1 only).""" - type: Literal["image", "done", "error"] - data: bytes | None # Image data for type="image" - error: str | None # Error message for type="error" - -# Model types -ImageModel = Literal["dall-e-2", "dall-e-3", "gpt-image-1", "gpt-image-1-mini"] - -# File types -FileTypes = Union[ - FileContent, # File-like object - tuple[str | None, FileContent], # (filename, content) - tuple[str | None, FileContent, str | None] # (filename, content, content_type) -] -``` - -## Model Comparison - -| Feature | DALL-E 3 | GPT-Image-1 | DALL-E 2 | -|---------|----------|-------------|----------| -| Generation Quality | Highest | Very High | Good | -| Max Prompt Length (generate) | 4000 chars | N/A | 1000 chars | -| Max Prompt Length (edit) | N/A | 32000 chars | 1000 chars | -| Images per Request (generate) | 1 only | N/A | 1-10 | -| Images per Request (edit) | N/A | Up to 16 input | 1 input | -| Generation Sizes | 1024x1024, 1792x1024, 1024x1792 | N/A | 256x256, 512x512, 1024x1024 | -| Edit Sizes | N/A | 256x256-1536x1024, auto | 256x256-1024x1024 | -| Quality Options | standard, hd | standard, low, medium, high, auto | N/A | -| Style Options | vivid, natural | N/A | N/A | -| Prompt Revision | Yes | N/A | No | -| Image Generation | Yes | No | Yes | -| Image Editing | No | Yes | Yes | -| Variations | No | No | Yes | -| Multiple Input Images | N/A | Yes (up to 16) | No | -| Output Formats | png | png, jpeg, webp | png | -| Transparency Control | N/A | Yes | Limited | -| Input Fidelity Control | N/A | Yes | No | -| Streaming Support | No | Yes | No | -| Compression Control | No | Yes | No | - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Use detailed, specific prompts -response = client.images.generate( - model="dall-e-3", - prompt="A photorealistic portrait of a golden retriever wearing " - "aviator sunglasses, sitting in a red vintage convertible, " - "with a sunny beach in the background, shot on 35mm film" -) - -# 2. Check revised prompts (DALL-E 3) -response = client.images.generate( - model="dall-e-3", - prompt="A dog" -) -print(f"Revised: {response.data[0].revised_prompt}") - -# 3. Use appropriate quality for use case -# Standard for drafts/iterations -response = client.images.generate( - model="dall-e-3", - prompt="Concept art", - quality="standard" -) - -# HD for final production -response = client.images.generate( - model="dall-e-3", - prompt="Final poster design", - quality="hd" -) - -# 4. Handle errors gracefully -from openai import BadRequestError - -try: - response = client.images.generate( - prompt="inappropriate content" - ) -except BadRequestError as e: - if "content_policy_violation" in str(e): - print("Prompt violated content policy") - else: - print(f"Error: {e}") -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def generate_image(): - client = AsyncOpenAI() - - response = await client.images.generate( - model="dall-e-3", - prompt="A futuristic robot", - size="1024x1024" - ) - - return response.data[0].url - -# Run async -image_url = asyncio.run(generate_image()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/index.md b/.tessl/tiles/tessl/pypi-openai/docs/index.md deleted file mode 100644 index 9ad8dc886..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/index.md +++ /dev/null @@ -1,2971 +0,0 @@ -# OpenAI Python Library - -The official Python client library for the OpenAI API, providing comprehensive access to OpenAI's suite of AI models including GPT-4, DALL-E, Whisper, and Embeddings. The library features both synchronous and asynchronous implementations, complete type definitions, streaming support, structured output parsing, and integration with OpenAI's latest features including the Assistants API, Realtime API, and advanced capabilities like function calling and vision inputs. - -## Package Information - -- **Package Name**: openai -- **Language**: Python -- **Installation**: `pip install openai` -- **Python Version**: 3.9+ -- **Official Documentation**: https://platform.openai.com/docs/api-reference - -## Quick Start for Agents - -```python -# Minimal working example - most common agent use case -from openai import OpenAI - -client = OpenAI() # Reads OPENAI_API_KEY from environment - -# Simple completion -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] -) -print(response.choices[0].message.content) - -# With error handling (production pattern) -from openai import OpenAIError, RateLimitError, APIError - -try: - response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) - content = response.choices[0].message.content -except RateLimitError as e: - # Handle rate limiting with exponential backoff - print(f"Rate limited: {e}") -except APIError as e: - # Handle API errors - print(f"API error: {e}") -except OpenAIError as e: - # Handle all other OpenAI errors - print(f"Error: {e}") -``` - -## Core Imports - -```python -# Essential imports for most use cases -from openai import OpenAI, AsyncOpenAI -from openai import AzureOpenAI, AsyncAzureOpenAI # For Azure - -# Common types and utilities -from typing import Callable, Iterable, Awaitable, Mapping, Literal, AsyncGenerator -import httpx -from openai import ( - Stream, AsyncStream, Client, AsyncClient, - NOT_GIVEN, NotGiven, not_given, Omit, omit, - AssistantEventHandler, AsyncAssistantEventHandler, - HttpxBinaryResponseContent, RequestOptions, Timeout, - APIResponse, AsyncAPIResponse -) -from openai._types import FileTypes -from openai.types.websocket_connection_options import WebsocketConnectionOptions - -# Chat completion types (most commonly used) -from openai.types.chat import ( - ChatCompletion, - ChatCompletionMessage, - ChatCompletionMessageParam, - ChatCompletionToolUnionParam, - ChatCompletionToolChoiceOptionParam, - completion_create_params, -) - -# Pagination types -from openai.pagination import ( - SyncPage, AsyncPage, - SyncCursorPage, AsyncCursorPage, - SyncConversationCursorPage, AsyncConversationCursorPage -) - -# Helpers for audio recording and playback -from openai.helpers import Microphone, LocalAudioPlayer - -# Access all type definitions -from openai import types -# Examples: types.ChatCompletion, types.Embedding, types.FileObject -# Use types.chat.ChatCompletionMessageParam for nested types - -# Error handling imports -from openai import ( - OpenAIError, APIError, APIStatusError, - APITimeoutError, APIConnectionError, APIResponseValidationError, - BadRequestError, AuthenticationError, PermissionDeniedError, - NotFoundError, ConflictError, UnprocessableEntityError, - RateLimitError, InternalServerError, - LengthFinishReasonError, ContentFilterFinishReasonError -) -``` - -## Basic Usage - -```python -from openai import OpenAI - -# Initialize the client (reads OPENAI_API_KEY from environment by default) -client = OpenAI(api_key="your-api-key") # Or omit api_key to use env var - -# Create a chat completion -response = client.chat.completions.create( - model="gpt-4", - messages=[ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Hello!"} - ] -) - -print(response.choices[0].message.content) - -# Async usage -import asyncio -from openai import AsyncOpenAI - -async def main(): - client = AsyncOpenAI(api_key="your-api-key") - response = await client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] - ) - print(response.choices[0].message.content) - -asyncio.run(main()) -``` - -## Module-Level Configuration (Legacy) - -The library supports a legacy module-level configuration pattern for backward compatibility. You can configure the default client by setting module-level variables: - -```python -import openai - -# API Configuration -openai.api_key = "your-api-key" -openai.organization = "org-xxx" -openai.project = "proj-xxx" -openai.webhook_secret = "whsec_xxx" - -# Network Configuration -openai.base_url = "https://api.openai.com/v1" -openai.timeout = 60.0 # seconds -openai.max_retries = 3 -openai.default_headers = {"Custom-Header": "value"} -openai.default_query = {"custom_param": "value"} -openai.http_client = custom_httpx_client - -# Azure OpenAI Configuration -openai.api_type = "azure" # "openai" or "azure" -openai.api_version = "2024-02-01" -openai.azure_endpoint = "https://your-resource.openai.azure.com" -openai.azure_ad_token = "your-ad-token" -openai.azure_ad_token_provider = lambda: get_token() - -# After setting these, you can use module-level methods: -# (This pattern is deprecated; prefer explicit client instantiation) -``` - -**Note**: The module-level configuration pattern is legacy. For new code, prefer explicit client instantiation: - -```python -from openai import OpenAI - -client = OpenAI( - api_key="your-api-key", - organization="org-xxx", - timeout=60.0 -) -``` - -## Architecture - -The OpenAI Python library is structured around client-resource architecture: - -- **Clients**: `OpenAI`, `AsyncOpenAI`, `AzureOpenAI`, `AsyncAzureOpenAI` - Entry points for API access with configuration -- **Resources**: Organized API endpoints (chat, audio, images, files, etc.) accessible as client attributes -- **Types**: Comprehensive Pydantic models for all requests and responses with full type safety -- **Streaming**: First-class streaming support via `Stream` and `AsyncStream` classes -- **Error Handling**: Structured exception hierarchy for different error types - -The library provides both synchronous and asynchronous implementations for all operations, enabling integration into any Python application architecture. - -## Capabilities - -### Client Initialization - -Initialize OpenAI clients with API credentials and configuration options for both OpenAI and Azure OpenAI services. - -```python { .api } -class OpenAI: - """Synchronous client for OpenAI API.""" - def __init__( - self, - *, - api_key: str | None | Callable[[], str] = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | httpx.URL | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - ): ... - -class AsyncOpenAI: - """Asynchronous client for OpenAI API.""" - def __init__( - self, - *, - api_key: str | None | Callable[[], Awaitable[str]] = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | httpx.URL | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - ): ... - -class AzureOpenAI: - """Synchronous client for Azure OpenAI Service.""" - def __init__( - self, - *, - api_version: str | None = None, - azure_endpoint: str | None = None, - azure_deployment: str | None = None, - api_key: str | None | Callable[[], str] = None, - azure_ad_token: str | None = None, - azure_ad_token_provider: Callable[[], str] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | httpx.URL | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - ): ... - -class AsyncAzureOpenAI: - """Asynchronous client for Azure OpenAI Service.""" - def __init__( - self, - *, - api_version: str | None = None, - azure_endpoint: str | None = None, - azure_deployment: str | None = None, - api_key: str | None | Callable[[], Awaitable[str]] = None, - azure_ad_token: str | None = None, - azure_ad_token_provider: Callable[[], Awaitable[str] | str] | None = None, - organization: str | None = None, - project: str | None = None, - webhook_secret: str | None = None, - base_url: str | httpx.URL | None = None, - websocket_base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - max_retries: int = 2, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - ): ... - -# Aliases for convenience -Client = OpenAI -AsyncClient = AsyncOpenAI -``` - -[Client Initialization](./client-initialization.md) - -### Chat Completions - -Create conversational responses using OpenAI's language models with support for text, function calling, vision inputs, audio, and structured output parsing. - -```python { .api } -def create( - self, - *, - messages: Iterable[ChatCompletionMessageParam], - model: str | ChatModel, - audio: dict | Omit = omit, - frequency_penalty: float | Omit = omit, - function_call: str | dict | Omit = omit, - functions: Iterable[dict] | Omit = omit, - logit_bias: dict[str, int] | Omit = omit, - logprobs: bool | Omit = omit, - top_logprobs: int | Omit = omit, - max_completion_tokens: int | Omit = omit, - max_tokens: int | Omit = omit, - metadata: dict[str, str] | Omit = omit, - modalities: list[Literal["text", "audio"]] | Omit = omit, - n: int | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - prediction: dict | Omit = omit, - presence_penalty: float | Omit = omit, - prompt_cache_key: str | Omit = omit, - prompt_cache_retention: Literal["in-memory", "24h"] | Omit = omit, - reasoning_effort: str | Omit = omit, - response_format: completion_create_params.ResponseFormat | Omit = omit, - safety_identifier: str | Omit = omit, - seed: int | Omit = omit, - service_tier: Literal["auto", "default", "flex", "scale", "priority"] | Omit = omit, - stop: str | list[str] | Omit = omit, - store: bool | Omit = omit, - stream: bool | Omit = omit, - stream_options: dict | Omit = omit, - temperature: float | Omit = omit, - tool_choice: ChatCompletionToolChoiceOptionParam | Omit = omit, - tools: Iterable[ChatCompletionToolUnionParam] | Omit = omit, - top_p: float | Omit = omit, - user: str | Omit = omit, - verbosity: Literal["low", "medium", "high"] | Omit = omit, - web_search_options: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, -) -> ChatCompletion | Stream[ChatCompletionChunk]: ... - -def parse( - self, - *, - messages: Iterable[ChatCompletionMessageParam], - model: str | ChatModel, - response_format: Type[BaseModel], - **kwargs -) -> ParsedChatCompletion: ... -``` - -[Chat Completions](./chat-completions.md) - -### Text Completions - -Generate text completions using legacy completion models. - -```python { .api } -def create( - self, - *, - model: str, - prompt: str | list[str] | list[int] | list[list[int]] | None, - best_of: int | Omit = omit, - echo: bool | Omit = omit, - frequency_penalty: float | Omit = omit, - logit_bias: dict[str, int] | Omit = omit, - logprobs: int | Omit = omit, - max_tokens: int | Omit = omit, - n: int | Omit = omit, - presence_penalty: float | Omit = omit, - seed: int | Omit = omit, - stop: str | list[str] | Omit = omit, - stream_options: Optional[dict] | Omit = omit, - stream: bool | Omit = omit, - suffix: str | Omit = omit, - temperature: float | Omit = omit, - top_p: float | Omit = omit, - user: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, -) -> Completion | Stream[Completion]: ... -``` - -[Text Completions](./completions.md) - -### Embeddings - -Create vector embeddings for text inputs to use in semantic search, clustering, and other ML applications. - -```python { .api } -def create( - self, - *, - input: str | list[str] | list[int] | list[list[int]], - model: str | EmbeddingModel, - dimensions: int | Omit = omit, - encoding_format: Literal["float", "base64"] | Omit = omit, - user: str | Omit = omit, - extra_headers: Mapping[str, str] | None = None, - extra_query: Mapping[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, -) -> CreateEmbeddingResponse: ... -``` - -[Embeddings](./embeddings.md) - -### Audio - -Convert audio to text (transcription and translation) and text to speech using Whisper and TTS models. - -```python { .api } -# Transcription -def create( - self, - *, - file: FileTypes, - model: str | AudioModel, - chunking_strategy: Optional[dict] | Omit = omit, - include: list[str] | Omit = omit, - known_speaker_names: list[str] | Omit = omit, - known_speaker_references: list[str] | Omit = omit, - language: str | Omit = omit, - prompt: str | Omit = omit, - response_format: str | Omit = omit, - stream: bool | Omit = omit, - temperature: float | Omit = omit, - timestamp_granularities: list[str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, -) -> Transcription | TranscriptionVerbose | TranscriptionDiarized | Stream[TranscriptionStreamEvent]: ... - -# Translation -def create( - self, - *, - file: FileTypes, - model: str | AudioModel, - prompt: str | Omit = omit, - response_format: str | Omit = omit, - temperature: float | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, -) -> Translation | TranslationVerbose: ... - -# Text-to-Speech -def create( - self, - *, - input: str, - model: str | SpeechModel, - voice: str, - instructions: str | Omit = omit, - response_format: str | Omit = omit, - speed: float | Omit = omit, - stream_format: Literal["sse", "audio"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, -) -> HttpxBinaryResponseContent: ... -``` - -[Audio](./audio.md) - -### Images - -Generate, edit, and create variations of images using DALL-E models. - -```python { .api } -def generate( - self, - *, - prompt: str, - background: Literal["transparent", "opaque", "auto"] | None | Omit = omit, - model: str | ImageModel | None = None, - moderation: Literal["low", "auto"] | None | Omit = omit, - n: int | None = None, - output_compression: int | None | Omit = omit, - output_format: Literal["png", "jpeg", "webp"] | None | Omit = omit, - partial_images: int | None | Omit = omit, - quality: str | None = None, - response_format: str | None = None, - size: str | None = None, - stream: bool | None | Omit = omit, - style: str | None = None, - user: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ImagesResponse: ... - -def edit( - self, - *, - image: FileTypes | list[FileTypes], - prompt: str, - background: Literal["transparent", "opaque", "auto"] | None | Omit = omit, - input_fidelity: Literal["high", "low"] | None | Omit = omit, - mask: FileTypes | None = None, - model: str | ImageModel | None = None, - n: int | None = None, - output_compression: int | None | Omit = omit, - output_format: Literal["png", "jpeg", "webp"] | None | Omit = omit, - partial_images: int | None | Omit = omit, - quality: Literal["standard", "low", "medium", "high", "auto"] | None | Omit = omit, - response_format: str | None = None, - size: str | None = None, - stream: bool | None | Omit = omit, - user: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ImagesResponse: ... - -def create_variation( - self, - *, - image: FileTypes, - model: str | ImageModel | None = None, - n: int | None = None, - response_format: str | None = None, - size: str | None = None, - user: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ImagesResponse: ... -``` - -[Images](./images.md) - -### Videos - -Generate and manipulate videos using video generation models. - -```python { .api } -def create( - self, - *, - prompt: str, - input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, - seconds: VideoSeconds | Omit = omit, - size: VideoSize | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: ... - -def create_and_poll( - self, - *, - prompt: str, - input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, - seconds: VideoSeconds | Omit = omit, - size: VideoSize | Omit = omit, - poll_interval_ms: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: ... - -def poll( - self, - video_id: str, - *, - poll_interval_ms: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: ... - -def retrieve( - self, - video_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Video: ... - -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Video]: ... - -def delete( - self, - video_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VideoDeleteResponse: ... - -def download_content( - self, - video_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> HttpxBinaryResponseContent: ... - -def remix( - self, - video_id: str, - *, - prompt: str, - seconds: VideoSeconds | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: ... -``` - -[Videos](./videos.md) - -### Files - -Upload and manage files for use with various OpenAI features like Assistants, Fine-tuning, and Batch processing. - -```python { .api } -def create( - self, - *, - file: FileTypes, - purpose: str | FilePurpose, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FileObject: ... - -def retrieve( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FileObject: ... - -def list( - self, - *, - purpose: str | None = None, - limit: int | None = None, - order: str | None = None, - after: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncPage[FileObject]: ... - -def delete( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FileDeleted: ... - -def content( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> HttpxBinaryResponseContent: ... - -def retrieve_content( - self, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> HttpxBinaryResponseContent: ... - -def wait_for_processing( - self, - file_id: str, - *, - poll_interval: float = 5.0, - max_wait: float = 3600.0, -) -> FileObject: ... -``` - -[Files](./files.md) - -### Uploads - -Upload large files in chunks for use with Assistants and other features. Upload parts are managed through the `.parts` subresource. - -```python { .api } -def create( - self, - *, - bytes: int, - filename: str, - mime_type: str, - purpose: FilePurpose, - expires_after: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Upload: ... - -def complete( - self, - upload_id: str, - *, - part_ids: list[str], - md5: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Upload: ... - -def cancel( - self, - upload_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Upload: ... - -def upload_file_chunked( - self, - *, - file: str | os.PathLike | bytes, - mime_type: str, - purpose: FilePurpose, - filename: str | None = None, - bytes: int | None = None, - part_size: int | None = None, - md5: str | Omit = omit, -) -> Upload: ... - -# Access via client.uploads.parts -def create( - self, - upload_id: str, - *, - data: FileTypes, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> UploadPart: ... -``` - -[Uploads](./uploads.md) - -### Models - -Retrieve information about available models and manage fine-tuned models. - -```python { .api } -def retrieve( - self, - model: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Model: ... - -def list( - self, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncPage[Model]: ... - -def delete( - self, - model: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ModelDeleted: ... -``` - -[Models](./models.md) - -### Fine-tuning - -Create and manage fine-tuning jobs to customize models on your own data. Fine-tuning operations are accessed through the `.jobs` and `.checkpoints` subresources. - -```python { .api } -# Access via client.fine_tuning.jobs -def create( - self, - *, - model: str, - training_file: str, - hyperparameters: dict | None = None, - method: dict | None = None, - integrations: list[dict] | None = None, - seed: int | None = None, - suffix: str | None = None, - validation_file: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FineTuningJob: ... - -def retrieve( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FineTuningJob: ... - -def list( - self, - *, - after: str | None = None, - limit: int | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[FineTuningJob]: ... - -def cancel( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FineTuningJob: ... - -def pause( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FineTuningJob: ... - -def resume( - self, - fine_tuning_job_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> FineTuningJob: ... - -def list_events( - self, - fine_tuning_job_id: str, - *, - after: str | None = None, - limit: int | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[FineTuningJobEvent]: ... - -# Access via client.fine_tuning.checkpoints -def list( - self, - fine_tuning_job_id: str, - *, - after: str | None = None, - limit: int | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[FineTuningJobCheckpoint]: ... -``` - -[Fine-tuning](./fine-tuning.md) - -### Moderations - -Check content against OpenAI's usage policies to detect potentially harmful content. - -```python { .api } -def create( - self, - *, - input: str | list[str], - model: str | ModerationModel | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ModerationCreateResponse: ... -``` - -[Moderations](./moderations.md) - -### Batch Processing - -Submit batch requests for asynchronous processing of multiple API calls. - -```python { .api } -def create( - self, - *, - completion_window: str, - endpoint: str, - input_file_id: str, - metadata: dict[str, str] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Batch: ... - -def retrieve( - self, - batch_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Batch: ... - -def list( - self, - *, - after: str | None = None, - limit: int | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[Batch]: ... - -def cancel( - self, - batch_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Batch: ... -``` - -[Batch Processing](./batches.md) - -### Vector Stores - -Create and manage vector stores for semantic search and retrieval with the Assistants API. Vector store files are managed through the `.files` subresource, and file batches through the `.file_batches` subresource. - -```python { .api } -def create( - self, - *, - file_ids: list[str] | None = None, - name: str | None = None, - expires_after: dict | None = None, - chunking_strategy: dict | None = None, - metadata: dict[str, str] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStore: ... - -def retrieve( - self, - vector_store_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStore: ... - -def update( - self, - vector_store_id: str, - *, - name: str | None = None, - expires_after: dict | None = None, - metadata: dict[str, str] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStore: ... - -def list( - self, - *, - after: str | None = None, - before: str | None = None, - limit: int | None = None, - order: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[VectorStore]: ... - -def delete( - self, - vector_store_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreDeleted: ... - -def search( - self, - vector_store_id: str, - *, - query: str, - limit: int | None = None, - filter: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreSearchResponse: ... - -# Access via client.vector_stores.files -def create( - self, - vector_store_id: str, - *, - file_id: str, - chunking_strategy: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreFile: ... - -def retrieve( - self, - vector_store_id: str, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreFile: ... - -def list( - self, - vector_store_id: str, - *, - after: str | None = None, - before: str | None = None, - filter: str | None = None, - limit: int | None = None, - order: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[VectorStoreFile]: ... - -def delete( - self, - vector_store_id: str, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreFileDeleted: ... - -# Access via client.vector_stores.file_batches -def create( - self, - vector_store_id: str, - *, - file_ids: list[str], - chunking_strategy: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreFileBatch: ... - -def retrieve( - self, - vector_store_id: str, - batch_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreFileBatch: ... - -def cancel( - self, - vector_store_id: str, - batch_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> VectorStoreFileBatch: ... - -def list_files( - self, - vector_store_id: str, - batch_id: str, - *, - after: str | None = None, - before: str | None = None, - filter: str | None = None, - limit: int | None = None, - order: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[VectorStoreFile]: ... -``` - -[Vector Stores](./vector-stores.md) - -### Assistants API (Beta) - -Build AI assistants with advanced capabilities including code interpreter, file search, and function calling. - -```python { .api } -def create( - self, - *, - model: str, - description: str | None = None, - instructions: str | None = None, - metadata: dict[str, str] | None = None, - name: str | None = None, - response_format: dict | None = None, - temperature: float | None = None, - tool_resources: dict | None = None, - tools: list[dict] | None = None, - top_p: float | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Assistant: ... - -def retrieve( - self, - assistant_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Assistant: ... - -def update( - self, - assistant_id: str, - *, - description: str | None = None, - instructions: str | None = None, - metadata: dict[str, str] | None = None, - model: str | None = None, - name: str | None = None, - response_format: dict | None = None, - temperature: float | None = None, - tool_resources: dict | None = None, - tools: list[dict] | None = None, - top_p: float | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Assistant: ... - -def list( - self, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Assistant]: ... - -def delete( - self, - assistant_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> AssistantDeleted: ... -``` - -[Assistants API](./assistants.md) - -### Threads and Messages (Beta) - -Create conversational threads and manage messages within the Assistants API. - -```python { .api } -# Threads -def create( - self, - *, - messages: list[dict] | None = None, - metadata: dict[str, str] | None = None, - tool_resources: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Thread: ... - -def retrieve( - self, - thread_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Thread: ... - -def update( - self, - thread_id: str, - *, - metadata: dict[str, str] | None = None, - tool_resources: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Thread: ... - -def delete( - self, - thread_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ThreadDeleted: ... - -def create_and_run( - self, - *, - assistant_id: str, - instructions: str | None = None, - metadata: dict[str, str] | None = None, - model: str | None = None, - thread: dict | None = None, - tools: list[dict] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def create_and_run_poll( - self, - *, - assistant_id: str, - poll_interval_ms: int | None = None, - instructions: str | None = None, - metadata: dict[str, str] | None = None, - model: str | None = None, - thread: dict | None = None, - tools: list[dict] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def create_and_run_stream( - self, - *, - assistant_id: str, - event_handler: AssistantEventHandler | None = None, - instructions: str | None = None, - metadata: dict[str, str] | None = None, - model: str | None = None, - thread: dict | None = None, - tools: list[dict] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> AssistantStreamManager: ... - -# Messages -def create( - self, - thread_id: str, - *, - role: str, - content: str | list[dict], - attachments: list[dict] | None = None, - metadata: dict[str, str] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Message: ... - -def retrieve( - self, - thread_id: str, - message_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Message: ... - -def update( - self, - thread_id: str, - message_id: str, - *, - metadata: dict[str, str] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Message: ... - -def list( - self, - thread_id: str, - *, - after: str | None = None, - before: str | None = None, - limit: int | None = None, - order: str | None = None, - run_id: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[Message]: ... - -def delete( - self, - thread_id: str, - message_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> MessageDeleted: ... -``` - -[Threads and Messages](./threads-messages.md) - -### Runs (Beta) - -Execute assistants on threads and handle tool calls. Run steps are managed through the `.steps` subresource. - -```python { .api } -def create( - self, - thread_id: str, - *, - assistant_id: str, - additional_instructions: str | None = None, - additional_messages: list[dict] | None = None, - instructions: str | None = None, - max_completion_tokens: int | None = None, - max_prompt_tokens: int | None = None, - metadata: dict[str, str] | None = None, - model: str | None = None, - parallel_tool_calls: bool | None = None, - response_format: dict | None = None, - stream: bool | None = None, - temperature: float | None = None, - tool_choice: str | dict | None = None, - tools: list[dict] | None = None, - top_p: float | None = None, - truncation_strategy: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def retrieve( - self, - thread_id: str, - run_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def update( - self, - thread_id: str, - run_id: str, - *, - metadata: dict[str, str] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def list( - self, - thread_id: str, - *, - after: str | None = None, - before: str | None = None, - limit: int | None = None, - order: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[Run]: ... - -def cancel( - self, - thread_id: str, - run_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def submit_tool_outputs( - self, - thread_id: str, - run_id: str, - *, - tool_outputs: list[dict], - stream: bool | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Run: ... - -def stream( - self, - thread_id: str, - *, - assistant_id: str, - event_handler: AssistantEventHandler | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> AssistantStreamManager: ... - -# Access via client.beta.threads.runs.steps -def retrieve( - self, - thread_id: str, - run_id: str, - step_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> RunStep: ... - -def list( - self, - thread_id: str, - run_id: str, - *, - after: str | None = None, - before: str | None = None, - limit: int | None = None, - order: str | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> SyncCursorPage[RunStep]: ... -``` - -[Runs](./runs.md) - -### ChatKit (Beta) - -Simplified, high-level interface for building chat applications with session and thread management. - -```python { .api } -def create( - self, - *, - user: str, - workflow: ChatSessionWorkflowParam, - chatkit_configuration: ChatSessionChatKitConfigurationParam | Omit = omit, - expires_after: ChatSessionExpiresAfterParam | Omit = omit, - rate_limits: ChatSessionRateLimitsParam | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ChatSession: ... -``` - -[ChatKit](./chatkit.md) - -### Realtime API (Beta) - -WebSocket-based realtime communication for low-latency conversational AI experiences. Realtime client secrets and calls are managed through the `.client_secrets` and `.calls` subresources. - -```python { .api } -def connect( - self, - *, - call_id: str | Omit = omit, - model: str | Omit = omit, - extra_query: dict[str, object] = {}, - extra_headers: dict[str, str] = {}, - websocket_connection_options: WebsocketConnectionOptions = {}, -) -> RealtimeConnectionManager: ... - -# Access via client.realtime.client_secrets -def create( - self, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ClientSecret: ... - -# Access via client.realtime.calls -def create( - self, - *, - model: str, - call_config: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Call: ... - -def retrieve( - self, - call_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Call: ... - -def update( - self, - call_id: str, - *, - call_config: dict | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Call: ... - -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Call]: ... -``` - -[Realtime API](./realtime.md) - -### Responses API - -Create responses with advanced tool support including computer use, file search, and code patching. Response input items and tokens are managed through the `.input_items` and `.input_tokens` subresources. - -```python { .api } -def create( - self, - *, - model: str, - input: dict | list[dict], - instructions: str | None = None, - metadata: dict[str, str] | None = None, - parallel_tool_calls: bool | None = None, - reasoning_effort: str | None = None, - store: bool | None = None, - stream: bool | None = None, - temperature: float | None = None, - tool_choice: str | dict | None = None, - tools: list[dict] | None = None, - top_p: float | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Response: ... - -def stream( - self, - *, - model: str, - input: dict | list[dict], - instructions: str | None = None, - metadata: dict[str, str] | None = None, - parallel_tool_calls: bool | None = None, - reasoning_effort: str | None = None, - store: bool | None = None, - temperature: float | None = None, - tool_choice: str | dict | None = None, - tools: list[dict] | None = None, - top_p: float | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Stream[ResponseStreamEvent]: ... - -def parse( - self, - *, - model: str, - input: dict | list[dict], - response_format: Type[BaseModel], - instructions: str | None = None, - metadata: dict[str, str] | None = None, - parallel_tool_calls: bool | None = None, - reasoning_effort: str | None = None, - store: bool | None = None, - temperature: float | None = None, - tool_choice: str | dict | None = None, - tools: list[dict] | None = None, - top_p: float | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ParsedResponse: ... - -def retrieve( - self, - response_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Response: ... - -def delete( - self, - response_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ResponseDeleted: ... - -def cancel( - self, - response_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> Response: ... - -# Access via client.responses.input_items -def create( - self, - response_id: str, - *, - type: str, - content: str | list[dict] | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ResponseInputItem: ... - -def delete( - self, - response_id: str, - item_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ResponseInputItemDeleted: ... - -# Access via client.responses.input_tokens -def create( - self, - response_id: str, - *, - token: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ResponseInputToken: ... - -def delete( - self, - response_id: str, - token_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | None = None, -) -> ResponseInputTokenDeleted: ... -``` - -[Responses](./responses.md) - -### Evaluations - -Create and manage evaluations to test model performance with custom testing criteria. Evaluation runs are managed through the `.runs` subresource. - -```python { .api } -def create( - self, - *, - data_source_config: dict, - testing_criteria: Iterable[dict], - metadata: dict[str, str] | None | Omit = omit, - name: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Eval: ... - -def retrieve( - self, - eval_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Eval: ... - -def update( - self, - eval_id: str, - *, - metadata: dict[str, str] | None | Omit = omit, - name: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Eval: ... - -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - order_by: Literal["created_at", "updated_at"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Eval]: ... - -def delete( - self, - eval_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> EvalDeleteResponse: ... - -# Access via client.evals.runs -def create( - self, - eval_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> EvalRun: ... - -def retrieve( - self, - eval_id: str, - run_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> EvalRun: ... - -def list( - self, - eval_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[EvalRun]: ... -``` - -[Evaluations](./evals.md) - -### Conversations - -Create and manage conversations for structured multi-turn interactions. Conversation items are managed through the `.items` subresource. - -```python { .api } -def create( - self, - *, - items: Iterable[dict] | None | Omit = omit, - metadata: dict[str, str] | None | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: ... - -def retrieve( - self, - conversation_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: ... - -def update( - self, - conversation_id: str, - *, - metadata: dict[str, str] | None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Conversation: ... - -def delete( - self, - conversation_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationDeleted: ... - -# Access via client.conversations.items -def create( - self, - conversation_id: str, - *, - type: str, - content: str | list[dict] | None = None, - metadata: dict[str, str] | None | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationItem: ... - -def retrieve( - self, - conversation_id: str, - item_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationItem: ... - -def update( - self, - conversation_id: str, - item_id: str, - *, - content: str | list[dict] | None = None, - metadata: dict[str, str] | None | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationItem: ... - -def list( - self, - conversation_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[ConversationItem]: ... - -def delete( - self, - conversation_id: str, - item_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ConversationItemDeleted: ... -``` - -[Conversations](./conversations.md) - -### Containers - -Create and manage isolated file storage containers for organizing files. Container files are managed through the `.files` subresource. - -```python { .api } -def create( - self, - *, - name: str, - expires_after: dict | Omit = omit, - file_ids: list[str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Container: ... - -def retrieve( - self, - container_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Container: ... - -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Container]: ... - -def delete( - self, - container_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> None: ... - -# Access via client.containers.files -def create( - self, - container_id: str, - *, - file: FileTypes, - purpose: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ContainerFile: ... - -def retrieve( - self, - container_id: str, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ContainerFile: ... - -def list( - self, - container_id: str, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[ContainerFile]: ... - -def delete( - self, - container_id: str, - file_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ContainerFileDeleted: ... -``` - -[Containers](./containers.md) - -### Webhooks - -Verify and handle webhook events from OpenAI for asynchronous notifications. - -```python { .api } -def verify_signature( - payload: str | bytes, - headers: dict[str, str] | list[tuple[str, str]], - *, - secret: str | None = None, - tolerance: int = 300 -) -> None: ... - -def unwrap( - payload: str | bytes, - headers: dict[str, str] | list[tuple[str, str]], - *, - secret: str | None = None -) -> UnwrapWebhookEvent: ... -``` - -[Webhooks](./webhooks.md) - -## Response Wrappers - -All resources support response wrapper patterns for accessing raw HTTP responses or streaming responses without loading them into memory. - -### Raw Response Access - -Access the underlying `httpx.Response` object for any API call: - -```python -from openai import OpenAI - -client = OpenAI() - -# Use .with_raw_response prefix -response = client.with_raw_response.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello!"}] -) - -# Access parsed response -completion = response.parsed - -# Access raw HTTP response -http_response = response.http_response -print(f"Status: {http_response.status_code}") -print(f"Headers: {http_response.headers}") -print(f"Raw content: {http_response.content}") -``` - -Available on all resources: - -```python -client.with_raw_response.chat.completions.create(...) -client.with_raw_response.audio.transcriptions.create(...) -client.with_raw_response.images.generate(...) -# ... and all other resources -``` - -### Streaming Response Access - -Stream responses without loading them into memory, useful for large responses: - -```python -response = client.with_streaming_response.files.content("file-abc123") - -# Stream chunks -for chunk in response.iter_bytes(): - process_chunk(chunk) - -# Or write directly to file -with open("output.txt", "wb") as f: - for chunk in response.iter_bytes(): - f.write(chunk) -``` - -Available on all resources: - -```python -client.with_streaming_response.chat.completions.create(...) -client.with_streaming_response.files.content(...) -# ... and all other resources -``` - -Both patterns work with async clients: - -```python -from openai import AsyncOpenAI - -client = AsyncOpenAI() - -# Raw response (async) -response = await client.with_raw_response.chat.completions.create(...) - -# Streaming response (async) -response = await client.with_streaming_response.files.content("file-abc123") -async for chunk in response.iter_bytes(): - process_chunk(chunk) -``` - -## Error Handling - -The library provides a comprehensive exception hierarchy for handling different error scenarios: - -```python { .api } -class OpenAIError(Exception): - """Base exception for all OpenAI errors.""" - -class APIError(OpenAIError): - """Base for API-related errors.""" - -class APIStatusError(APIError): - """HTTP status code errors (4xx, 5xx).""" - status_code: int - response: httpx.Response - body: object - -class APITimeoutError(APIError): - """Request timeout errors.""" - -class APIConnectionError(APIError): - """Connection errors.""" - -class APIResponseValidationError(APIError): - """Response validation errors.""" - -class BadRequestError(APIStatusError): - """400 Bad Request.""" - -class AuthenticationError(APIStatusError): - """401 Authentication error.""" - -class PermissionDeniedError(APIStatusError): - """403 Permission denied.""" - -class NotFoundError(APIStatusError): - """404 Not found.""" - -class ConflictError(APIStatusError): - """409 Conflict.""" - -class UnprocessableEntityError(APIStatusError): - """422 Unprocessable Entity.""" - -class RateLimitError(APIStatusError): - """429 Rate limit exceeded.""" - -class InternalServerError(APIStatusError): - """500+ Server errors.""" - -class LengthFinishReasonError(OpenAIError): - """Raised when completion stops due to reaching max tokens.""" - -class ContentFilterFinishReasonError(OpenAIError): - """Raised when completion stops due to content filtering.""" - -class InvalidWebhookSignatureError(OpenAIError): - """Raised when webhook signature verification fails.""" -``` - -### Production Error Handling Patterns - -```python -from openai import OpenAI, OpenAIError, RateLimitError, APIError, APIConnectionError -import time - -client = OpenAI() - -# Pattern 1: Retry with exponential backoff for rate limits -def create_completion_with_retry(messages, max_retries=3): - for attempt in range(max_retries): - try: - return client.chat.completions.create( - model="gpt-4", - messages=messages - ) - except RateLimitError as e: - if attempt == max_retries - 1: - raise - wait_time = (2 ** attempt) + 1 # Exponential backoff - print(f"Rate limited. Retrying in {wait_time}s...") - time.sleep(wait_time) - except APIConnectionError as e: - if attempt == max_retries - 1: - raise - wait_time = 2 ** attempt - print(f"Connection error. Retrying in {wait_time}s...") - time.sleep(wait_time) - -# Pattern 2: Handle all error types -def safe_completion(messages): - try: - response = client.chat.completions.create( - model="gpt-4", - messages=messages - ) - return response.choices[0].message.content - except RateLimitError as e: - # Handle rate limiting - implement backoff - return f"Rate limited: {e}" - except APIConnectionError as e: - # Handle connection issues - check network - return f"Connection error: {e}" - except APIError as e: - # Handle general API errors - return f"API error: {e}" - except OpenAIError as e: - # Catch all other OpenAI errors - return f"OpenAI error: {e}" - except Exception as e: - # Catch unexpected errors - return f"Unexpected error: {e}" - -# Pattern 3: Check finish reason -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Write a long story"}], - max_tokens=100 -) - -finish_reason = response.choices[0].finish_reason -if finish_reason == "length": - print("Response truncated - increase max_tokens") -elif finish_reason == "content_filter": - print("Content filtered - adjust prompt") -elif finish_reason == "stop": - print("Completed normally") -``` - -## Common Types - -```python { .api } -# Sentinel values for omitted parameters -NOT_GIVEN: Omit # Sentinel indicating parameter was not provided -not_given: Omit # Alias for NOT_GIVEN (lowercase convention) - -# Omit type for optional API parameters -# Use Omit type with omit default value instead of None for optional parameters -# This distinguishes between "parameter not provided" vs "parameter set to null" -Omit: TypeAlias # Type for omittable parameters -omit: Omit # Sentinel value for omitted parameters - -# Type utilities -NoneType: Type[None] # Used for response casting when no response expected - -# Common type aliases -# File upload types - can be specified in multiple formats: -# - Raw file content (bytes, file object, etc.) -# - (filename, content) tuple -# - (filename, content, mime_type) tuple -FileTypes = Union[ - FileContent, # Just the file bytes/buffer - Tuple[Optional[str], FileContent], # (filename, content) - Tuple[Optional[str], FileContent, Optional[str]] # (filename, content, mime_type) -] - -# Timeout can be a float (seconds) or an httpx.Timeout object for fine-grained control -Timeout = Union[float, httpx.Timeout] - -# Azure AD Token Provider -# Callable that returns Azure AD tokens for authentication with Azure OpenAI -# Can be sync or async function -AzureADTokenProvider = Callable[[], str] | Callable[[], Awaitable[str]] - -# Default configuration constants -DEFAULT_TIMEOUT: httpx.Timeout # Default timeout: 600s total, 5s connect -DEFAULT_MAX_RETRIES: int # Default maximum retries: 2 -DEFAULT_CONNECTION_LIMITS: httpx.Limits # Default connection limits: 1000 max connections, 100 keepalive - -# Type aliases for specific domains -VideoModel = Literal["sora-2", "sora-2-pro"] -VideoSeconds = int # Duration in seconds -VideoSize = str # e.g., "720x1280" - -# Request configuration -class RequestOptions(TypedDict, total=False): - """Options for individual API requests.""" - extra_headers: dict[str, str] - extra_query: dict[str, object] - extra_body: dict[str, object] - timeout: float | httpx.Timeout - -# WebSocket configuration for Realtime API -class WebsocketConnectionOptions(TypedDict, total=False): - """WebSocket connection options for realtime API connections.""" - extensions: Sequence[ClientExtensionFactory] | None # List of supported extensions - subprotocols: Sequence[Subprotocol] | None # List of supported subprotocols - compression: str | None # Compression setting ("permessage-deflate" enabled by default, None to disable) - max_size: int | None # Maximum size of incoming messages in bytes - max_queue: int | None | tuple[int | None, int | None] # High-water mark of receive buffer - write_limit: int | tuple[int, int | None] # High-water mark of write buffer in bytes - -# Response wrappers -class HttpxBinaryResponseContent: - """Binary response content for audio, images, etc.""" - content: bytes - response: httpx.Response - def read(self) -> bytes: ... - def write_to_file(self, file: str | os.PathLike) -> None: ... - -# Pagination types -class SyncPage[T]: - """Standard pagination.""" - data: list[T] - object: str - def __iter__(self) -> Iterator[T]: ... - def __next__(self) -> T: ... - -class AsyncPage[T]: - """Async pagination.""" - data: list[T] - object: str - def __aiter__(self) -> AsyncIterator[T]: ... - def __anext__(self) -> T: ... - -class SyncCursorPage[T]: - """Cursor-based pagination.""" - data: list[T] - has_more: bool - def __iter__(self) -> Iterator[T]: ... - def __next__(self) -> T: ... - -class AsyncCursorPage[T]: - """Async cursor-based pagination.""" - data: list[T] - has_more: bool - def __aiter__(self) -> AsyncIterator[T]: ... - def __anext__(self) -> T: ... - -class SyncConversationCursorPage[T]: - """Conversation cursor-based pagination.""" - data: list[T] - has_more: bool - last_id: str | None - def __iter__(self) -> Iterator[T]: ... - def __next__(self) -> T: ... - -class AsyncConversationCursorPage[T]: - """Async conversation cursor-based pagination.""" - data: list[T] - has_more: bool - last_id: str | None - def __aiter__(self) -> AsyncIterator[T]: ... - def __anext__(self) -> T: ... - -# Streaming types -class Stream[T]: - """Synchronous streaming.""" - def __iter__(self) -> Iterator[T]: ... - def __next__(self) -> T: ... - def __enter__(self) -> Stream[T]: ... - def __exit__(self, *args) -> None: ... - def close(self) -> None: ... - -class AsyncStream[T]: - """Asynchronous streaming.""" - def __aiter__(self) -> AsyncIterator[T]: ... - def __anext__(self) -> T: ... - async def __aenter__(self) -> AsyncStream[T]: ... - async def __aexit__(self, *args) -> None: ... - async def close(self) -> None: ... -``` - -## Helper Utilities - -```python { .api } -# Package Information -__title__: str # Package name ("openai") -VERSION: str # Library version string -__version__: str # Library version (same as VERSION) - -# Pydantic Base Model -BaseModel # Base class for creating custom Pydantic models (from pydantic import BaseModel) - -# HTTP Client Classes -class DefaultHttpxClient: - """Default synchronous httpx client with connection pooling.""" - -class DefaultAsyncHttpxClient: - """Default asynchronous httpx client with connection pooling.""" - -class DefaultAioHttpClient: - """Alternative async HTTP client using aiohttp.""" - -# Additional Type Exports -Transport: type # HTTP transport type (httpx.BaseTransport) -ProxiesTypes: type # Proxy configuration type - -# File and Function Utilities -def file_from_path(path: str) -> FileTypes: - """ - Load a file from filesystem path for upload to OpenAI API. - - Args: - path: Filesystem path to the file - - Returns: - FileTypes: A (filename, content, mime_type) tuple suitable for API upload - """ - -def pydantic_function_tool( - model: Type[BaseModel], - *, - name: str | None = None, - description: str | None = None -) -> ChatCompletionToolParam: - """ - Create a function tool definition from a Pydantic model for use with chat completions. - - Args: - model: Pydantic model class defining the function parameters schema - name: Optional function name (defaults to model class name in snake_case) - description: Optional function description (defaults to model docstring) - - Returns: - ChatCompletionToolParam: A tool definition dict with 'type' and 'function' keys. - Type from openai.types.chat module. - Can be passed to chat.completions.create(tools=[...]) - """ - -class AssistantEventHandler: - """Base class for handling assistant streaming events.""" - def on_event(self, event) -> None: ... - def on_run_step_created(self, run_step) -> None: ... - def on_run_step_done(self, run_step) -> None: ... - def on_tool_call_created(self, tool_call) -> None: ... - def on_tool_call_done(self, tool_call) -> None: ... - def on_message_created(self, message) -> None: ... - def on_message_done(self, message) -> None: ... - def on_text_created(self, text) -> None: ... - def on_text_delta(self, delta, snapshot) -> None: ... - def on_text_done(self, text) -> None: ... - -class AsyncAssistantEventHandler: - """Base class for handling assistant streaming events asynchronously.""" - async def on_event(self, event) -> None: ... - async def on_run_step_created(self, run_step) -> None: ... - async def on_run_step_done(self, run_step) -> None: ... - async def on_tool_call_created(self, tool_call) -> None: ... - async def on_tool_call_done(self, tool_call) -> None: ... - async def on_message_created(self, message) -> None: ... - async def on_message_done(self, message) -> None: ... - async def on_text_created(self, text) -> None: ... - async def on_text_delta(self, delta, snapshot) -> None: ... - async def on_text_done(self, text) -> None: ... - -# Audio Recording Helper -class Microphone(Generic[DType]): - """ - Microphone helper for recording audio input from default audio device. - - Requires optional dependencies: numpy, sounddevice - Install with: pip install openai[voice_helpers] - - Type Parameters: - DType: numpy dtype for audio data (default: np.int16) - """ - def __init__( - self, - channels: int = 1, - dtype: Type[DType] = np.int16, - should_record: Callable[[], bool] | None = None, - timeout: float | None = None, - ): - """ - Initialize microphone for recording. - - Args: - channels: Number of audio channels (1 for mono, 2 for stereo) - dtype: Numpy data type for audio samples - should_record: Optional callback function that returns True while recording should continue - timeout: Maximum recording duration in seconds (None for unlimited) - """ - - async def record( - self, - return_ndarray: bool | None = False - ) -> npt.NDArray[DType] | FileTypes: - """ - Record audio from microphone. - - Args: - return_ndarray: If True, return numpy array; if False, return WAV file tuple - - Returns: - Either a numpy array of audio samples or FileTypes tuple (filename, buffer, mime_type) - suitable for passing to OpenAI API methods. - """ - -# Audio Playback Helper -class LocalAudioPlayer: - """ - Local audio player for playing audio content through default audio device. - - Requires optional dependencies: numpy, sounddevice - Install with: pip install openai[voice_helpers] - - The player uses a fixed sample rate of 24000 Hz, 1 channel (mono), and float32 dtype. - """ - def __init__( - self, - should_stop: Callable[[], bool] | None = None, - ): - """ - Initialize audio player. - - Args: - should_stop: Optional callback function that returns True to stop playback - """ - - async def play( - self, - input: npt.NDArray[np.int16] | npt.NDArray[np.float32] | HttpxBinaryResponseContent | AsyncStreamedBinaryAPIResponse | StreamedBinaryAPIResponse - ) -> None: - """ - Play audio data through local audio device. - - Args: - input: Audio data as numpy array (int16 or float32) or response content from TTS API - """ - - async def play_stream( - self, - buffer_stream: AsyncGenerator[npt.NDArray[np.float32] | npt.NDArray[np.int16] | None, None] - ) -> None: - """ - Stream and play audio data as it arrives. - - Useful for playing streaming audio responses from realtime API. - - Args: - buffer_stream: Async generator yielding audio buffers (numpy arrays) or None to signal completion - """ -``` - -## Agent Integration Patterns - -### Pattern 1: Simple Question-Answer Agent - -```python -from openai import OpenAI, OpenAIError - -class SimpleAgent: - def __init__(self, api_key: str | None = None): - self.client = OpenAI(api_key=api_key) - self.model = "gpt-4" - - def ask(self, question: str) -> str: - """Ask a single question and get an answer.""" - try: - response = self.client.chat.completions.create( - model=self.model, - messages=[{"role": "user", "content": question}] - ) - return response.choices[0].message.content - except OpenAIError as e: - return f"Error: {e}" - -# Usage -agent = SimpleAgent() -answer = agent.ask("What is the capital of France?") -print(answer) -``` - -### Pattern 2: Conversational Agent with Memory - -```python -from openai import OpenAI -from typing import List, Dict - -class ConversationalAgent: - def __init__(self, system_prompt: str, api_key: str | None = None): - self.client = OpenAI(api_key=api_key) - self.model = "gpt-4" - self.messages: List[Dict[str, str]] = [ - {"role": "system", "content": system_prompt} - ] - - def chat(self, user_message: str) -> str: - """Send a message and maintain conversation history.""" - self.messages.append({"role": "user", "content": user_message}) - - response = self.client.chat.completions.create( - model=self.model, - messages=self.messages - ) - - assistant_message = response.choices[0].message.content - self.messages.append({"role": "assistant", "content": assistant_message}) - - return assistant_message - - def reset(self): - """Clear conversation history (keep system prompt).""" - self.messages = [self.messages[0]] - -# Usage -agent = ConversationalAgent("You are a helpful Python expert.") -print(agent.chat("How do I read a file?")) -print(agent.chat("And how do I write to it?")) # Maintains context -``` - -### Pattern 3: Function-Calling Agent - -```python -from openai import OpenAI -import json - -class FunctionAgent: - def __init__(self, api_key: str | None = None): - self.client = OpenAI(api_key=api_key) - self.model = "gpt-4" - - # Define available functions - self.tools = [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get current weather for a location", - "parameters": { - "type": "object", - "properties": { - "location": {"type": "string", "description": "City name"}, - "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]} - }, - "required": ["location"] - } - } - } - ] - - def execute_function(self, name: str, arguments: str) -> str: - """Execute the actual function call.""" - args = json.loads(arguments) - - if name == "get_weather": - # Implement actual weather lookup - location = args["location"] - unit = args.get("unit", "celsius") - return f"The weather in {location} is 22°{unit[0].upper()}" - - return f"Unknown function: {name}" - - def run(self, user_query: str) -> str: - """Run agent with function calling capability.""" - messages = [{"role": "user", "content": user_query}] - - # Initial request - response = self.client.chat.completions.create( - model=self.model, - messages=messages, - tools=self.tools, - tool_choice="auto" - ) - - response_message = response.choices[0].message - messages.append(response_message) - - # Handle function calls - if response_message.tool_calls: - for tool_call in response_message.tool_calls: - function_name = tool_call.function.name - function_args = tool_call.function.arguments - - # Execute function - function_response = self.execute_function(function_name, function_args) - - # Add function response to messages - messages.append({ - "role": "tool", - "tool_call_id": tool_call.id, - "content": function_response - }) - - # Get final response - final_response = self.client.chat.completions.create( - model=self.model, - messages=messages - ) - return final_response.choices[0].message.content - - return response_message.content - -# Usage -agent = FunctionAgent() -result = agent.run("What's the weather in London?") -print(result) -``` - -### Pattern 4: Structured Output Agent - -```python -from openai import OpenAI -from pydantic import BaseModel -from typing import List - -class Task(BaseModel): - """A single task.""" - title: str - description: str - priority: int # 1-5 - -class TaskList(BaseModel): - """A list of tasks.""" - tasks: List[Task] - -class StructuredAgent: - def __init__(self, api_key: str | None = None): - self.client = OpenAI(api_key=api_key) - self.model = "gpt-4" - - def extract_tasks(self, text: str) -> TaskList: - """Extract structured task list from text.""" - completion = self.client.beta.chat.completions.parse( - model=self.model, - messages=[ - {"role": "system", "content": "Extract tasks from the user's text."}, - {"role": "user", "content": text} - ], - response_format=TaskList - ) - - return completion.choices[0].message.parsed - -# Usage -agent = StructuredAgent() -text = "I need to finish the report by Friday, call the client tomorrow, and review the code this afternoon." -tasks = agent.extract_tasks(text) - -for task in tasks.tasks: - print(f"[P{task.priority}] {task.title}: {task.description}") -``` - -### Pattern 5: Streaming Agent - -```python -from openai import OpenAI -from typing import Generator - -class StreamingAgent: - def __init__(self, api_key: str | None = None): - self.client = OpenAI(api_key=api_key) - self.model = "gpt-4" - - def stream_response(self, prompt: str) -> Generator[str, None, None]: - """Stream response token by token.""" - stream = self.client.chat.completions.create( - model=self.model, - messages=[{"role": "user", "content": prompt}], - stream=True - ) - - for chunk in stream: - if chunk.choices[0].delta.content: - yield chunk.choices[0].delta.content - - def chat_stream(self, prompt: str) -> str: - """Chat with streaming, return complete response.""" - full_response = [] - for chunk in self.stream_response(prompt): - print(chunk, end="", flush=True) - full_response.append(chunk) - print() # Newline after stream - return "".join(full_response) - -# Usage -agent = StreamingAgent() -response = agent.chat_stream("Tell me a short story about AI.") -``` - -## Troubleshooting - -### Common Issues and Solutions - -#### Issue: "APIError: The api_key client option must be set" -**Solution**: Set the OPENAI_API_KEY environment variable or pass api_key explicitly: -```python -# Option 1: Environment variable -import os -os.environ["OPENAI_API_KEY"] = "your-key" -client = OpenAI() - -# Option 2: Explicit parameter -client = OpenAI(api_key="your-key") -``` - -#### Issue: Rate limiting (429 errors) -**Solution**: Implement exponential backoff retry logic (see error handling patterns above). - -#### Issue: "InvalidRequestError: This model's maximum context length is X tokens" -**Solution**: Reduce message history or use a model with larger context: -```python -# Truncate old messages -max_messages = 10 -messages = messages[-max_messages:] - -# Or use a larger context model -model = "gpt-4-turbo" # 128K context -``` - -#### Issue: Streaming responses cut off -**Solution**: Check finish_reason and handle properly: -```python -stream = client.chat.completions.create(model="gpt-4", messages=messages, stream=True) -for chunk in stream: - if chunk.choices[0].finish_reason == "length": - print("[Response truncated - increase max_tokens]") - elif chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") -``` - -#### Issue: Async code not working -**Solution**: Ensure you're using AsyncOpenAI and await properly: -```python -from openai import AsyncOpenAI -import asyncio - -async def main(): - client = AsyncOpenAI() - response = await client.chat.completions.create(...) - print(response.choices[0].message.content) - -asyncio.run(main()) -``` - -#### Issue: File uploads failing -**Solution**: Use proper file handling with context managers: -```python -from openai import OpenAI - -client = OpenAI() - -# Correct approach -with open("file.txt", "rb") as f: - file_obj = client.files.create(file=f, purpose="assistants") - -# Or use file_from_path helper -from openai import file_from_path -file_obj = client.files.create(file=file_from_path("file.txt"), purpose="assistants") -``` - -#### Issue: Azure OpenAI authentication errors -**Solution**: Verify you're using the correct authentication method: -```python -from openai import AzureOpenAI - -# Correct for Azure API key -client = AzureOpenAI( - api_key="your-azure-key", # NOT OpenAI key - azure_endpoint="https://your-resource.openai.azure.com/", - api_version="2024-02-15-preview" -) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/models.md b/.tessl/tiles/tessl/pypi-openai/docs/models.md deleted file mode 100644 index 1b202182c..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/models.md +++ /dev/null @@ -1,243 +0,0 @@ -# Models - -Retrieve information about available models and manage fine-tuned models. List models, get model details, and delete custom models. - -## Capabilities - -### Retrieve Model - -Get details about a specific model. - -```python { .api } -def retrieve( - self, - model: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Model: - """ - Retrieve model information. - - Args: - model: Model ID (e.g., "gpt-4", "gpt-3.5-turbo", custom fine-tuned model). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Model: Model details including ID, owner, and capabilities. - - Raises: - NotFoundError: Model not found - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Get model details -model = client.models.retrieve("gpt-4") - -print(f"ID: {model.id}") -print(f"Owner: {model.owned_by}") -print(f"Created: {model.created}") -``` - -### List Models - -List all available models. - -```python { .api } -def list( - self, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncPage[Model]: - """ - List all available models. - - Args: - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncPage[Model]: Paginated list of models. - """ -``` - -Usage examples: - -```python -# List all models -models = client.models.list() - -for model in models.data: - print(f"{model.id} - {model.owned_by}") - -# Filter for specific models -gpt4_models = [m for m in client.models.list() if "gpt-4" in m.id] - -# Find fine-tuned models -fine_tuned = [m for m in client.models.list() if "ft:" in m.id] - -for model in fine_tuned: - print(f"Fine-tuned model: {model.id}") -``` - -### Delete Model - -Delete a fine-tuned model. - -```python { .api } -def delete( - self, - model: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ModelDeleted: - """ - Delete a fine-tuned model. Only works with custom fine-tuned models. - - Args: - model: The model ID to delete (must be a fine-tuned model you own). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ModelDeleted: Deletion confirmation. - - Raises: - NotFoundError: Model not found - BadRequestError: Cannot delete base models - """ -``` - -Usage example: - -```python -# Delete fine-tuned model -result = client.models.delete("ft:gpt-3.5-turbo:custom:abc123") - -print(f"Deleted: {result.id}") -print(f"Success: {result.deleted}") - -# Note: Cannot delete base models -try: - client.models.delete("gpt-4") # Will fail -except Exception as e: - print(f"Error: {e}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Model(BaseModel): - """Model information.""" - id: str - created: int - object: Literal["model"] - owned_by: str - -class ModelDeleted(BaseModel): - """Model deletion confirmation.""" - id: str - deleted: bool - object: Literal["model"] - -# Pagination -class SyncPage[T](BaseModel): - data: list[T] - object: str - def __iter__(self) -> Iterator[T]: ... -``` - -## Common Models - -### Chat Models -- `gpt-4o`: Most capable GPT-4 Omni model -- `gpt-4-turbo`: Fast GPT-4 with 128K context -- `gpt-4`: Original GPT-4 model -- `gpt-3.5-turbo`: Fast and cost-effective - -### Reasoning Models -- `o1`: Advanced reasoning model -- `o3`: Latest reasoning model - -### Embedding Models -- `text-embedding-3-large`: Largest embedding model -- `text-embedding-3-small`: Efficient embedding model -- `text-embedding-ada-002`: Legacy embedding model - -### Other Models -- `whisper-1`: Audio transcription/translation -- `tts-1`, `tts-1-hd`: Text-to-speech -- `dall-e-3`, `dall-e-2`: Image generation -- `text-moderation-latest`: Content moderation - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Cache model list to reduce API calls -def get_available_models(): - return [m.id for m in client.models.list()] - -# 2. Verify model availability before use -def is_model_available(model_id: str) -> bool: - try: - client.models.retrieve(model_id) - return True - except: - return False - -# 3. List your fine-tuned models -def get_my_fine_tuned_models(): - return [ - m for m in client.models.list() - if "ft:" in m.id - ] - -# 4. Clean up old fine-tuned models -old_models = get_my_fine_tuned_models() -for model in old_models: - if should_delete(model): - client.models.delete(model.id) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def list_models(): - client = AsyncOpenAI() - models = await client.models.list() - return [m.id for m in models.data] - -model_ids = asyncio.run(list_models()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/moderations.md b/.tessl/tiles/tessl/pypi-openai/docs/moderations.md deleted file mode 100644 index b0a0b7334..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/moderations.md +++ /dev/null @@ -1,356 +0,0 @@ -# Moderations - -Check content against OpenAI's usage policies to detect potentially harmful content across multiple categories including hate speech, violence, sexual content, and self-harm. Supports both text and image inputs for multi-modal moderation. - -## Capabilities - -### Create Moderation - -Classify text and/or image content for policy violations. - -```python { .api } -def create( - self, - *, - input: str | list[str] | list[ModerationMultiModalInputParam], - model: str | ModerationModel | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ModerationCreateResponse: - """ - Classify text and/or image inputs against OpenAI's usage policies. - - Args: - input: Content to classify. Can be: - - Single string: "Text to moderate" - - List of strings: ["Text 1", "Text 2"] - - List of multi-modal inputs: [{"type": "text", "text": "..."}, - {"type": "image_url", "image_url": {"url": "..."}}] - Maximum 32,768 characters per text input. - - model: Moderation model to use. Options: - - "text-moderation-latest": Latest text model, automatically updated - - "text-moderation-stable": Stable text model, less frequent updates - - "omni-moderation-latest": Latest multi-modal model (supports text + images, default) - - "omni-moderation-2024-09-26": Specific omni model version - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ModerationCreateResponse: Contains flagged status and category scores - for each input. - - Raises: - BadRequestError: Input exceeds maximum length - AuthenticationError: Invalid API key - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Check single text -response = client.moderations.create( - input="I want to hurt someone" -) - -result = response.results[0] -print(f"Flagged: {result.flagged}") - -if result.flagged: - print("Violated categories:") - for category, flagged in result.categories.model_dump().items(): - if flagged: - score = getattr(result.category_scores, category) - print(f" {category}: {score:.4f}") - -# Check multiple texts -texts = [ - "Hello, how are you?", - "This is inappropriate content", - "What's the weather like today?" -] - -response = client.moderations.create(input=texts) - -for i, result in enumerate(response.results): - print(f"Text {i + 1}: {'Flagged' if result.flagged else 'Safe'}") - -# Use latest omni model -response = client.moderations.create( - model="omni-moderation-latest", - input="Check this message for violations" -) - -# Use stable model for consistent behavior -response = client.moderations.create( - model="text-moderation-stable", - input="Testing moderation" -) - -# Multi-modal moderation with text and images -response = client.moderations.create( - model="omni-moderation-latest", - input=[ - {"type": "text", "text": "Check this message"}, - { - "type": "image_url", - "image_url": {"url": "https://example.com/image.jpg"} - } - ] -) - -# Moderate image from base64 -import base64 - -with open("image.jpg", "rb") as f: - image_data = base64.b64encode(f.read()).decode() - -response = client.moderations.create( - model="omni-moderation-latest", - input=[ - { - "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{image_data}"} - } - ] -) - -# Detailed category analysis -response = client.moderations.create( - input="Potentially problematic text" -) - -result = response.results[0] - -# All categories and scores -categories = result.categories -scores = result.category_scores - -print("Category Analysis:") -print(f" Hate: {scores.hate:.4f} (flagged: {categories.hate})") -print(f" Hate/Threatening: {scores.hate_threatening:.4f} (flagged: {categories.hate_threatening})") -print(f" Harassment: {scores.harassment:.4f} (flagged: {categories.harassment})") -print(f" Harassment/Threatening: {scores.harassment_threatening:.4f} (flagged: {categories.harassment_threatening})") -print(f" Self-Harm: {scores.self_harm:.4f} (flagged: {categories.self_harm})") -print(f" Self-Harm/Intent: {scores.self_harm_intent:.4f} (flagged: {categories.self_harm_intent})") -print(f" Self-Harm/Instructions: {scores.self_harm_instructions:.4f} (flagged: {categories.self_harm_instructions})") -print(f" Sexual: {scores.sexual:.4f} (flagged: {categories.sexual})") -print(f" Sexual/Minors: {scores.sexual_minors:.4f} (flagged: {categories.sexual_minors})") -print(f" Violence: {scores.violence:.4f} (flagged: {categories.violence})") -print(f" Violence/Graphic: {scores.violence_graphic:.4f} (flagged: {categories.violence_graphic})") - -# Filter user content example -def is_safe_content(text: str) -> tuple[bool, list[str]]: - """ - Check if content is safe to use. - Returns (is_safe, violated_categories) - """ - response = client.moderations.create(input=text) - result = response.results[0] - - if not result.flagged: - return True, [] - - violated = [ - category for category, flagged in result.categories.model_dump().items() - if flagged - ] - - return False, violated - -# Use in application -user_input = "Some user-generated content" -is_safe, violations = is_safe_content(user_input) - -if is_safe: - print("Content approved") -else: - print(f"Content rejected. Violations: {', '.join(violations)}") -``` - -## Types - -```python { .api } -from typing import Literal, Union -from typing_extensions import TypedDict -from pydantic import BaseModel - -class ModerationCreateResponse(BaseModel): - """Moderation response.""" - id: str - model: str - results: list[ModerationResult] - -class ModerationResult(BaseModel): - """Single moderation result.""" - flagged: bool - categories: ModerationCategories - category_scores: ModerationCategoryScores - category_applied_input_types: ModerationCategoryAppliedInputTypes - -class ModerationCategories(BaseModel): - """Category flags (true if violated).""" - hate: bool - hate_threatening: bool - harassment: bool - harassment_threatening: bool - self_harm: bool - self_harm_intent: bool - self_harm_instructions: bool - sexual: bool - sexual_minors: bool - violence: bool - violence_graphic: bool - illicit: bool - illicit_violent: bool - -class ModerationCategoryScores(BaseModel): - """Confidence scores (0-1) for each category.""" - hate: float - hate_threatening: float - harassment: float - harassment_threatening: float - self_harm: float - self_harm_intent: float - self_harm_instructions: float - sexual: float - sexual_minors: float - violence: float - violence_graphic: float - illicit: float - illicit_violent: float - -class ModerationCategoryAppliedInputTypes(BaseModel): - """Input types that triggered each category.""" - hate: list[str] - hate_threatening: list[str] - harassment: list[str] - harassment_threatening: list[str] - self_harm: list[str] - self_harm_intent: list[str] - self_harm_instructions: list[str] - sexual: list[str] - sexual_minors: list[str] - violence: list[str] - violence_graphic: list[str] - illicit: list[str] - illicit_violent: list[str] - -# Model type -ModerationModel = Literal[ - "text-moderation-latest", - "text-moderation-stable", - "omni-moderation-latest", - "omni-moderation-2024-09-26" -] - -# Multi-modal input types -class ModerationTextInputParam(TypedDict): - """Text input for moderation.""" - text: str # Required: Text content to moderate - type: Literal["text"] # Required: Always "text" - -class ImageURL(TypedDict): - """Image URL or base64 data.""" - url: str # Required: URL or data:image/...;base64,... string - -class ModerationImageURLInputParam(TypedDict): - """Image input for moderation.""" - image_url: ImageURL # Required: Image URL or base64 data - type: Literal["image_url"] # Required: Always "image_url" - -# Union type for multi-modal inputs -ModerationMultiModalInputParam = Union[ - ModerationTextInputParam, - ModerationImageURLInputParam -] -``` - -## Category Descriptions - -| Category | Description | -|----------|-------------| -| hate | Content expressing, inciting, or promoting hate based on protected characteristics | -| hate/threatening | Hateful content that also includes violence or serious harm | -| harassment | Content harassing, bullying, or abusing an individual | -| harassment/threatening | Harassing content that also includes violence or serious harm | -| self-harm | Content promoting, encouraging, or depicting acts of self-harm | -| self-harm/intent | Content indicating intent to engage in self-harm | -| self-harm/instructions | Content providing instructions or advice for self-harm | -| sexual | Content meant to arouse sexual excitement | -| sexual/minors | Sexual content involving individuals under 18 | -| violence | Content depicting death, violence, or physical injury | -| violence/graphic | Graphic violent content with extreme detail | -| illicit | Content promoting illicit substances or illegal activities | -| illicit/violent | Illicit content involving violence | - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Moderate user-generated content before processing -def moderate_before_processing(user_text: str): - response = client.moderations.create(input=user_text) - - if response.results[0].flagged: - return None, "Content violates policies" - - # Safe to process - return process_safe_content(user_text), None - -# 2. Batch moderation for efficiency -user_messages = ["msg1", "msg2", "msg3"] -response = client.moderations.create(input=user_messages) - -safe_messages = [ - msg for msg, result in zip(user_messages, response.results) - if not result.flagged -] - -# 3. Log violations for analysis -for i, result in enumerate(response.results): - if result.flagged: - violated_categories = [ - cat for cat, flagged in result.categories.model_dump().items() - if flagged - ] - log_violation(user_messages[i], violated_categories) - -# 4. Use thresholds for borderline content -def is_definitely_safe(text: str, threshold: float = 0.5) -> bool: - response = client.moderations.create(input=text) - result = response.results[0] - - # Check if any score exceeds threshold - scores = result.category_scores.model_dump() - return all(score < threshold for score in scores.values()) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def moderate_content(text: str): - client = AsyncOpenAI() - - response = await client.moderations.create(input=text) - return response.results[0].flagged - -# Run async -is_flagged = asyncio.run(moderate_content("Check this text")) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/realtime.md b/.tessl/tiles/tessl/pypi-openai/docs/realtime.md deleted file mode 100644 index 3882b6129..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/realtime.md +++ /dev/null @@ -1,588 +0,0 @@ -# Realtime API - -WebSocket-based realtime communication for low-latency conversational AI experiences with audio streaming, function calling, and interruption handling. - -## Capabilities - -### Create Realtime Session - -Establish a WebSocket connection for realtime interaction. - -```python { .api } -def create( - self, - *, - model: str, - modalities: list[str] | Omit = omit, - instructions: str | Omit = omit, - voice: str | Omit = omit, - input_audio_format: str | Omit = omit, - output_audio_format: str | Omit = omit, - input_audio_transcription: dict | Omit = omit, - turn_detection: dict | Omit = omit, - tools: list[dict] | Omit = omit, - tool_choice: str | Omit = omit, - temperature: float | Omit = omit, - max_response_output_tokens: int | str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RealtimeSession: - """ - Create a realtime session for WebSocket communication. - - Args: - model: Model to use (e.g., "gpt-4o-realtime-preview"). - - modalities: Response modalities. Options: ["text", "audio"]. - - instructions: System instructions for the session. - - voice: Voice for audio output. Options: "alloy", "echo", "fable", - "onyx", "nova", "shimmer". - - input_audio_format: Input audio format. Options: "pcm16", "g711_ulaw", - "g711_alaw". - - output_audio_format: Output audio format. Options: "pcm16", "g711_ulaw", - "g711_alaw", "mp3", "opus". - - input_audio_transcription: Enable input transcription. - {"model": "whisper-1"} - - turn_detection: Turn detection configuration. - {"type": "server_vad", "threshold": 0.5, "prefix_padding_ms": 300, - "silence_duration_ms": 200} - - tools: Function tools available to the model. - - tool_choice: Tool choice configuration. "auto", "none", "required". - - temperature: Sampling temperature. - - max_response_output_tokens: Maximum output tokens per response. - - Returns: - RealtimeSession: WebSocket session configuration. - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create realtime session -session = client.beta.realtime.sessions.create( - model="gpt-4o-realtime-preview", - modalities=["text", "audio"], - voice="alloy", - instructions="You are a helpful assistant.", - input_audio_format="pcm16", - output_audio_format="pcm16", - turn_detection={ - "type": "server_vad", - "threshold": 0.5, - "silence_duration_ms": 500 - } -) - -# Access WebSocket URL -ws_url = session.client_secret.value - -# Use with WebSocket library (e.g., websockets) -import asyncio -import websockets -import json - -async def realtime_conversation(): - async with websockets.connect(ws_url) as websocket: - # Send audio input - await websocket.send(json.dumps({ - "type": "input_audio_buffer.append", - "audio": base64_audio_data - })) - - # Commit audio - await websocket.send(json.dumps({ - "type": "input_audio_buffer.commit" - })) - - # Receive responses - async for message in websocket: - event = json.loads(message) - - if event["type"] == "response.audio.delta": - # Handle audio chunk - audio_chunk = event["delta"] - # Play or process audio - - elif event["type"] == "response.text.delta": - # Handle text chunk - text = event["delta"] - print(text, end="", flush=True) - - elif event["type"] == "response.done": - break - -asyncio.run(realtime_conversation()) -``` - -### Realtime Calls - -Manage incoming and outgoing realtime voice calls with call control methods. - -```python { .api } -# Accessed via: client.realtime.calls or client.beta.realtime.calls -def create( - self, - *, - model: str, - modalities: list[str] | Omit = omit, - instructions: str | Omit = omit, - voice: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RealtimeCall: - """ - Initiate an outgoing realtime call. - - Args: - model: Model to use for the call. - modalities: Response modalities (["audio"] recommended). - instructions: System instructions for the call. - voice: Voice for audio output. - - Returns: - RealtimeCall: Call object with connection details. - """ - -def accept( - self, - call_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RealtimeCall: - """ - Accept an incoming realtime call. - - Args: - call_id: The ID of the incoming call to accept. - - Returns: - RealtimeCall: Call object with status "active". - """ - -def hangup( - self, - call_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RealtimeCall: - """ - End an active realtime call. - - Args: - call_id: The ID of the call to hang up. - - Returns: - RealtimeCall: Call object with status "completed". - """ - -def refer( - self, - call_id: str, - *, - refer_to: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RealtimeCall: - """ - Transfer a realtime call to another destination. - - Args: - call_id: The ID of the call to transfer. - refer_to: Destination identifier to transfer the call to. - - Returns: - RealtimeCall: Call object with status "referred". - """ - -def reject( - self, - call_id: str, - *, - reason: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RealtimeCall: - """ - Reject an incoming realtime call. - - Args: - call_id: The ID of the incoming call to reject. - reason: Optional reason for rejection. - - Returns: - RealtimeCall: Call object with status "rejected". - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create an outgoing call (available at both paths) -call = client.realtime.calls.create( - model="gpt-4o-realtime-preview", - modalities=["audio"], - voice="alloy", - instructions="You are a helpful phone assistant." -) - -print(f"Call ID: {call.id}, Status: {call.status}") - -# Accept an incoming call -accepted_call = client.realtime.calls.accept("call_abc123") - -# Transfer a call -referred_call = client.realtime.calls.refer( - call.id, - refer_to="destination_id" -) - -# End a call -ended_call = client.realtime.calls.hangup(call.id) -print(f"Call ended: {ended_call.status}") - -# Reject an incoming call -rejected_call = client.realtime.calls.reject( - "call_xyz789", - reason="User unavailable" -) -``` - -### Client Secrets - -Create ephemeral client secrets for secure realtime session establishment. - -```python { .api } -# Access via client.realtime.client_secrets - -def create( - self, - *, - expires_after: dict | Omit = omit, - session: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ClientSecretCreateResponse: - """ - Create a Realtime client secret with an associated session configuration. - - Args: - expires_after: Configuration for the client secret expiration. - Expiration refers to the time after which a client secret will - no longer be valid for creating sessions. The session itself may - continue after that time once started. A secret can be used to - create multiple sessions until it expires. - Example: {"anchor": "created_at", "seconds": 3600} - - session: Session configuration to use for the client secret. - Choose either a realtime session or a transcription session. - Example for realtime: { - "type": "realtime", - "model": "gpt-4o-realtime-preview", - "voice": "alloy", - "modalities": ["text", "audio"] - } - Example for transcription: { - "type": "transcription", - "model": "whisper-1" - } - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - ClientSecretCreateResponse: Created client secret with value and - expiration time. Use the secret value to establish WebSocket - connections from client-side applications. - - Notes: - - Client secrets enable secure browser-based realtime connections - - Secrets expire after specified duration - - One secret can establish multiple sessions until expiration - - Use for temporary, client-side authentication - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create client secret for realtime session -secret = client.realtime.client_secrets.create( - expires_after={ - "anchor": "created_at", - "seconds": 3600 # Expires in 1 hour - }, - session={ - "type": "realtime", - "model": "gpt-4o-realtime-preview", - "voice": "alloy", - "modalities": ["text", "audio"], - "instructions": "You are a helpful voice assistant." - } -) - -print(f"Client Secret: {secret.value}") -print(f"Expires At: {secret.expires_at}") - -# Create client secret for transcription session -transcription_secret = client.realtime.client_secrets.create( - expires_after={ - "anchor": "created_at", - "seconds": 1800 # Expires in 30 minutes - }, - session={ - "type": "transcription", - "model": "whisper-1", - "input_audio_format": "pcm16", - "input_audio_transcription": { - "model": "whisper-1" - } - } -) - -# Use the secret client-side for WebSocket connection -# The secret value is passed as authentication to establish the connection -``` - -### Transcription Sessions - -Create ephemeral API tokens for client-side realtime transcription applications. - -```python { .api } -def create( - self, - *, - client_secret: dict | Omit = omit, - include: list[str] | Omit = omit, - input_audio_format: Literal["pcm16", "g711_ulaw", "g711_alaw"] | Omit = omit, - input_audio_noise_reduction: dict | Omit = omit, - input_audio_transcription: dict | Omit = omit, - modalities: list[Literal["text", "audio"]] | Omit = omit, - turn_detection: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> TranscriptionSession: - """ - Create an ephemeral API token for client-side realtime transcriptions. - - Returns a session object with a client_secret containing an ephemeral - API token for authenticating browser clients. - - Args: - client_secret: Configuration options for the generated client secret. - - include: Items to include in the transcription. Options: - - "item.input_audio_transcription.logprobs" - - input_audio_format: Input audio format. Options: "pcm16", "g711_ulaw", - "g711_alaw". For pcm16, audio must be 16-bit PCM at 24kHz sample rate, - single channel (mono), little-endian byte order. - - input_audio_noise_reduction: Configuration for input audio noise reduction. - Filters audio added to the input buffer before VAD and model processing. - Can improve VAD accuracy and model performance. - - input_audio_transcription: Configuration for input audio transcription. - Can optionally set language and prompt for additional guidance. - - modalities: Response modalities. Options: ["text"], ["audio"], or both. - To disable audio, set to ["text"]. - - turn_detection: Configuration for turn detection (Server VAD or Semantic VAD). - Set to null to turn off, requiring manual trigger of model response. - Server VAD detects speech based on audio volume. Semantic VAD uses - a turn detection model to estimate turn completion and dynamically - sets timeout based on probability. - - Returns: - TranscriptionSession: Session with client_secret for browser authentication. - """ -``` - -**Usage Example:** - -```python -from openai import OpenAI - -client = OpenAI() - -# Create transcription session for client-side use -session = client.beta.realtime.transcription_sessions.create( - input_audio_format="pcm16", - input_audio_transcription={ - "model": "whisper-1", - "language": "en", - "prompt": "Technical discussion" - }, - input_audio_noise_reduction={ - "type": "default" - }, - turn_detection={ - "type": "semantic_vad", - "threshold": 0.6 - }, - modalities=["text", "audio"], - include=["item.input_audio_transcription.logprobs"] -) - -# Use the client_secret in browser/client application -ephemeral_token = session.client_secret.value -print(f"Session ID: {session.id}") -print(f"Token expires: {session.client_secret.expires_at}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class RealtimeSession(BaseModel): - """Realtime session configuration.""" - id: str - model: str - modalities: list[str] - instructions: str | None - voice: str | None - input_audio_format: str - output_audio_format: str - input_audio_transcription: dict | None - turn_detection: dict | None - tools: list[dict] | None - tool_choice: str - temperature: float | None - max_response_output_tokens: int | str | None - client_secret: ClientSecret - -class ClientSecret(BaseModel): - """WebSocket client secret.""" - value: str - expires_at: int - -class ClientSecretCreateResponse(BaseModel): - """Response from creating a client secret.""" - id: str - created_at: int - expires_at: int - value: str # The ephemeral client secret value - session: dict # Session configuration associated with this secret -``` - -## Event Types - -WebSocket events for realtime communication: - -- `session.created` - Session established -- `input_audio_buffer.append` - Add audio data -- `input_audio_buffer.commit` - Process buffered audio -- `input_audio_buffer.clear` - Clear buffer -- `conversation.item.create` - Add conversation item -- `response.create` - Request response -- `response.cancel` - Cancel current response -- `response.audio.delta` - Audio chunk received -- `response.text.delta` - Text chunk received -- `response.done` - Response completed -- `conversation.item.input_audio_transcription.completed` - Transcription ready -- `error` - Error occurred - -## Best Practices - -```python -import asyncio -import websockets -import json -import base64 - -async def realtime_session(session_url: str): - async with websockets.connect(session_url) as ws: - # Handle incoming events - async def receive_events(): - async for message in ws: - event = json.loads(message) - - if event["type"] == "response.audio.delta": - # Stream audio to speaker - audio_data = base64.b64decode(event["delta"]) - play_audio(audio_data) - - elif event["type"] == "response.text.delta": - # Display text - print(event["delta"], end="", flush=True) - - # Send audio input - async def send_audio(): - while True: - audio_chunk = record_audio_chunk() - await ws.send(json.dumps({ - "type": "input_audio_buffer.append", - "audio": base64.b64encode(audio_chunk).decode() - })) - await asyncio.sleep(0.1) - - # Run both tasks - await asyncio.gather( - receive_events(), - send_audio() - ) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_session(): - client = AsyncOpenAI() - - session = await client.beta.realtime.sessions.create( - model="gpt-4o-realtime-preview", - modalities=["audio"] - ) - - return session.client_secret.value - -ws_url = asyncio.run(create_session()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/responses.md b/.tessl/tiles/tessl/pypi-openai/docs/responses.md deleted file mode 100644 index 05867f490..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/responses.md +++ /dev/null @@ -1,581 +0,0 @@ -# Responses API - -Create responses with advanced tool support including computer use, file search, and code patching. The Responses API provides an enhanced interface for complex assistant interactions. - -## Capabilities - -### Create Response - -Generate a response with advanced tools and capabilities. - -```python { .api } -def create( - self, - *, - model: str, - input: dict | list[dict], - instructions: str | Omit = omit, - metadata: dict[str, str] | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - reasoning_effort: str | Omit = omit, - store: bool | Omit = omit, - stream: bool | Omit = omit, - temperature: float | Omit = omit, - tool_choice: str | dict | Omit = omit, - tools: list[dict] | Omit = omit, - top_p: float | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Response: - """ - Create a response with advanced tools. - - Args: - model: Model ID to use. - - input: Input message(s). Can be: - - Single message: {"role": "user", "content": "..."} - - List of messages: [{"role": "user", "content": "..."}, ...] - - instructions: System instructions for the response. - - metadata: Key-value pairs (max 16). - - parallel_tool_calls: Enable parallel tool execution. - - reasoning_effort: Reasoning level for reasoning models. - - store: If true, store response for later retrieval. - - stream: Enable streaming response. - - temperature: Sampling temperature. - - tool_choice: Tool choice configuration. - - tools: Available tools. Options: - - {"type": "function", "function": {...}}: Custom functions - - {"type": "file_search"}: File search - - {"type": "computer_use"}: Computer use (screen/keyboard/mouse) - - {"type": "apply_patch"}: Code patching - - top_p: Nucleus sampling. - - Returns: - Response: Generated response with tool results. - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic response -response = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Hello!"} -) - -print(response.output[0].content) - -# With file search -response = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Find information about Python."}, - tools=[{"type": "file_search"}] -) - -# With computer use (beta) -response = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Click the button."}, - tools=[{ - "type": "computer_use", - "display_width_px": 1920, - "display_height_px": 1080 - }] -) - -# With code patching -response = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Fix the bug in this code."}, - tools=[{"type": "apply_patch"}] -) - -# Multi-turn conversation -messages = [ - {"role": "user", "content": "What is 2+2?"}, - {"role": "assistant", "content": "4"}, - {"role": "user", "content": "What about 3+3?"} -] - -response = client.responses.create( - model="gpt-4", - input=messages -) - -# Streaming response -stream = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Tell me a story."}, - stream=True -) - -for chunk in stream: - if chunk.delta: - print(chunk.delta.content, end="", flush=True) -``` - -### Retrieve Response - -Get a stored response by ID. - -```python { .api } -def retrieve( - self, - response_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Response: - """Retrieve a stored response.""" -``` - -### Delete Response - -Delete a stored response by ID. - -```python { .api } -def delete( - self, - response_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> None: - """ - Delete a response with the given ID. - - Args: - response_id: The ID of the response to delete. - """ -``` - -### Cancel Response - -Cancel an in-progress response. - -```python { .api } -def cancel( - self, - response_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Response: - """ - Cancel an in-progress response. - - Only responses created with the `background` parameter set to `true` can be cancelled. - - Args: - response_id: The ID of the response to cancel. - """ -``` - -### Stream Response - -Stream a response in real-time, either creating a new response or streaming an existing one. - -```python { .api } -def stream( - self, - *, - response_id: str = omit, - input: str | dict | list[dict] | None = omit, - model: str | None = omit, - background: bool | None = omit, - text_format: type = omit, - tools: list[dict] | None = omit, - conversation: dict | None = omit, - include: list[str] | None = omit, - instructions: str | None = omit, - max_output_tokens: int | None = omit, - max_tool_calls: int | None = omit, - metadata: dict[str, str] | None = omit, - parallel_tool_calls: bool | None = omit, - previous_response_id: str | None = omit, - prompt: dict | None = omit, - prompt_cache_key: str | None = omit, - prompt_cache_retention: Literal["in-memory", "24h"] | None = omit, - reasoning: dict | None = omit, - safety_identifier: str | None = omit, - service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None = omit, - store: bool | None = omit, - stream_options: dict | None = omit, - temperature: float | None = omit, - text: dict | None = omit, - tool_choice: str | dict | None = omit, - top_logprobs: int | None = omit, - top_p: float | None = omit, - truncation: Literal["auto", "disabled"] | None = omit, - user: str | None = omit, - starting_after: int | None = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ResponseStreamManager: - """ - Stream a response. Can either create a new response or stream an existing one. - - Args: - response_id: If provided, stream an existing response. Otherwise, create a new response. - input: Input message(s) for creating a new response. - model: Model ID to use for creating a new response. - text_format: Parse streaming text into a specific format type. - starting_after: For streaming existing responses, start streaming after this item index. - - Returns: - ResponseStreamManager: Streaming response manager for iterating over chunks. - """ -``` - -### Parse Response - -Create a response with structured output parsing using a format type. - -```python { .api } -def parse( - self, - *, - text_format: type, - background: bool | None = omit, - conversation: dict | None = omit, - include: list[str] | None = omit, - input: str | dict | list[dict] | None = omit, - instructions: str | None = omit, - max_output_tokens: int | None = omit, - max_tool_calls: int | None = omit, - metadata: dict[str, str] | None = omit, - model: str | None = omit, - parallel_tool_calls: bool | None = omit, - previous_response_id: str | None = omit, - prompt: dict | None = omit, - prompt_cache_key: str | None = omit, - prompt_cache_retention: Literal["in-memory", "24h"] | None = omit, - reasoning: dict | None = omit, - safety_identifier: str | None = omit, - service_tier: Literal["auto", "default", "flex", "scale", "priority"] | None = omit, - store: bool | None = omit, - stream: Literal[False] | Literal[True] | None = omit, - stream_options: dict | None = omit, - temperature: float | None = omit, - text: dict | None = omit, - tool_choice: str | dict | None = omit, - tools: list[dict] | None = omit, - top_logprobs: int | None = omit, - top_p: float | None = omit, - truncation: Literal["auto", "disabled"] | None = omit, - user: str | None = omit, - verbosity: Literal["low", "medium", "high"] | None = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ParsedResponse: - """ - Create a response with structured output parsing. - - Args: - text_format: A type (Pydantic model or similar) to parse the response into. - input: Input message(s). - model: Model ID to use. - - Returns: - ParsedResponse: Response with parsed structured output. - """ -``` - -### Input Items - -List input items for a response. - -```python { .api } -def input_items.list( - self, - response_id: str, - *, - after: str | None = omit, - include: list[str] | None = omit, - limit: int | None = omit, - order: Literal["asc", "desc"] | None = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[ResponseItem]: - """ - Returns a list of input items for a given response. - - Args: - response_id: The ID of the response. - after: An item ID to list items after, used in pagination. - include: Additional fields to include in the response. - limit: Limit on number of objects (1-100, default 20). - order: Order to return items ('asc' or 'desc', default 'desc'). - - Returns: - Paginated list of response input items. - """ -``` - -### Input Tokens - -Count input tokens for a potential response. - -```python { .api } -def input_tokens.count( - self, - *, - conversation: dict | None = omit, - input: str | list[dict] | None = omit, - instructions: str | None = omit, - model: str | None = omit, - parallel_tool_calls: bool | None = omit, - previous_response_id: str | None = omit, - reasoning: dict | None = omit, - text: dict | None = omit, - tool_choice: dict | None = omit, - tools: list[dict] | None = omit, - truncation: Literal["auto", "disabled"] | None = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> InputTokenCountResponse: - """ - Get input token counts for a potential response without making the request. - - Args: - conversation: The conversation context. - input: Text, image, or file inputs. - instructions: System instructions. - model: Model ID. - tools: Available tools. - - Returns: - InputTokenCountResponse: Token count information. - """ -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Response(BaseModel): - """Response object.""" - id: str - created_at: int - model: str - object: Literal["response"] - output: list[ResponseContent] - status: Literal["in_progress", "completed", "failed", "cancelled"] - tool_calls: list[ToolCall] | None - usage: Usage | None - -class ResponseContent(BaseModel): - """Response content.""" - role: Literal["assistant"] - content: str - type: Literal["text"] - -class ToolCall(BaseModel): - """Tool invocation.""" - id: str - type: str - function: dict | None - computer_use: dict | None - apply_patch: dict | None - -class Usage(BaseModel): - """Token usage.""" - input_tokens: int - output_tokens: int - total_tokens: int -``` - -## Tool Types - -### Computer Use - -```python -{ - "type": "computer_use", - "display_width_px": 1920, - "display_height_px": 1080, - "display_number": 1 # Optional, for multi-monitor -} -``` - -Enables the model to interact with a computer: -- View screenshots -- Control mouse (move, click) -- Type on keyboard -- Execute commands - -### Apply Patch - -```python -{ - "type": "apply_patch" -} -``` - -Enables code modification: -- Generate code patches -- Apply changes to files -- Fix bugs -- Refactor code - -### File Search - -```python -{ - "type": "file_search" -} -``` - -Search uploaded files and documents. - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Use for complex multi-step tasks -response = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Analyze this codebase and fix all bugs."}, - tools=[ - {"type": "file_search"}, - {"type": "apply_patch"} - ] -) - -# 2. Handle tool calls -if response.tool_calls: - for tool_call in response.tool_calls: - if tool_call.type == "apply_patch": - # Review and apply patch - patch = tool_call.apply_patch - print(f"Patch: {patch}") - -# 3. Store important responses -response = client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Important analysis"}, - store=True -) - -# Retrieve later -stored = client.responses.retrieve(response.id) -``` - -### Compact Conversation - -Compact a conversation to reduce its size while maintaining context and essential information. This optimization utility helps manage token usage and costs for long conversations. - -```python { .api } -def compact( - self, - *, - model: str, - input: str | list[dict] | Omit = omit, - instructions: str | Omit = omit, - previous_response_id: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> CompactedResponse: - """ - Compact conversation to optimize token usage. - - This method compresses conversation history while preserving essential - context, enabling more efficient multi-turn conversations by reducing - token consumption. - - Args: - model: Model ID used to generate the response. OpenAI offers a - wide range of models with different capabilities and price points. - - input: Text, image, or file inputs to the model. Can be a string - or list of input item dictionaries. - - instructions: System (developer) message for the model's context. - When used with previous_response_id, previous instructions - are not carried over. - - previous_response_id: Unique ID of the previous response to create - multi-turn conversations. Cannot be used with conversation parameter. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - CompactedResponse: Compacted response with reduced token count. - """ -``` - -Usage example: - -```python -# Compact a long conversation -compacted = client.responses.compact( - model="gpt-4", - input="Summarize the key points from our previous discussion", - previous_response_id="resp-abc123" -) - -print(f"Original tokens: {compacted.original_token_count}") -print(f"Compacted tokens: {compacted.token_count}") -print(f"Reduction: {compacted.reduction_percentage}%") -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_response(): - client = AsyncOpenAI() - - response = await client.responses.create( - model="gpt-4", - input={"role": "user", "content": "Hello!"} - ) - - return response.output[0].content - -content = asyncio.run(create_response()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/runs.md b/.tessl/tiles/tessl/pypi-openai/docs/runs.md deleted file mode 100644 index 0bea9f294..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/runs.md +++ /dev/null @@ -1,725 +0,0 @@ -# Runs - -Execute assistants on threads and handle tool calls. Runs represent the execution of an assistant on a thread, managing the conversation flow and tool interactions. - -## Capabilities - -### Create Run - -Execute an assistant on a thread. - -```python { .api } -def create( - self, - thread_id: str, - *, - assistant_id: str, - additional_instructions: str | Omit = omit, - additional_messages: list[dict] | Omit = omit, - instructions: str | Omit = omit, - max_completion_tokens: int | Omit = omit, - max_prompt_tokens: int | Omit = omit, - metadata: dict[str, str] | Omit = omit, - model: str | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - response_format: dict | Omit = omit, - stream: bool | Omit = omit, - temperature: float | Omit = omit, - tool_choice: str | dict | Omit = omit, - tools: list[dict] | Omit = omit, - top_p: float | Omit = omit, - truncation_strategy: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """ - Run an assistant on a thread. - - Args: - thread_id: The thread ID. - assistant_id: The assistant ID. - additional_instructions: Append to assistant instructions. - additional_messages: Add messages before run. - instructions: Override assistant instructions. - max_completion_tokens: Maximum completion tokens. - max_prompt_tokens: Maximum prompt tokens. - metadata: Key-value pairs (max 16). - model: Override assistant model. - parallel_tool_calls: Enable parallel tool calls. - response_format: Override response format. - stream: Enable streaming. - temperature: Sampling temperature. - tool_choice: Tool choice configuration. - tools: Override assistant tools. - top_p: Nucleus sampling. - truncation_strategy: Message truncation config. - - Returns: - Run: Created run. - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Basic run -run = client.beta.threads.runs.create( - thread_id="thread_abc123", - assistant_id="asst_abc123" -) - -print(f"Run ID: {run.id}") -print(f"Status: {run.status}") - -# With additional instructions -run = client.beta.threads.runs.create( - thread_id="thread_abc123", - assistant_id="asst_abc123", - additional_instructions="Be concise." -) - -# Override model -run = client.beta.threads.runs.create( - thread_id="thread_abc123", - assistant_id="asst_abc123", - model="gpt-4-turbo" -) - -# With streaming -stream = client.beta.threads.runs.create( - thread_id="thread_abc123", - assistant_id="asst_abc123", - stream=True -) - -for event in stream: - print(event) -``` - -### Retrieve Run - -Get run status and details. - -```python { .api } -def retrieve( - self, - thread_id: str, - run_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """Get run details.""" -``` - -Usage example: - -```python -run = client.beta.threads.runs.retrieve( - thread_id="thread_abc123", - run_id="run_abc123" -) - -print(f"Status: {run.status}") -print(f"Model: {run.model}") -``` - -### Update Run - -Update a run's metadata. - -```python { .api } -def update( - self, - run_id: str, - *, - thread_id: str, - metadata: dict[str, str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """ - Modify a run's metadata. - - Args: - run_id: The run ID to update. - thread_id: The thread ID containing the run. - metadata: Set of 16 key-value pairs for storing additional information. - Keys max 64 characters, values max 512 characters. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Run: Updated run object. - """ -``` - -Usage example: - -```python -# Update run metadata -run = client.beta.threads.runs.update( - run_id="run_abc123", - thread_id="thread_abc123", - metadata={ - "user_id": "user-456", - "priority": "high" - } -) - -print(f"Updated metadata: {run.metadata}") -``` - -### List Runs - -List runs for a thread. - -```python { .api } -def list( - self, - thread_id: str, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Run]: - """List runs for a thread.""" -``` - -### Cancel Run - -Cancel an in-progress run. - -```python { .api } -def cancel( - self, - thread_id: str, - run_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """Cancel a run.""" -``` - -Usage example: - -```python -run = client.beta.threads.runs.cancel( - thread_id="thread_abc123", - run_id="run_abc123" -) - -print(f"Status: {run.status}") # "cancelling" or "cancelled" -``` - -### Submit Tool Outputs - -Submit results of tool calls back to the run. - -```python { .api } -def submit_tool_outputs( - self, - thread_id: str, - run_id: str, - *, - tool_outputs: list[dict], - stream: bool | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """ - Submit tool call results. - - Args: - thread_id: The thread ID. - run_id: The run ID. - tool_outputs: List of tool outputs. - [{"tool_call_id": "call_abc", "output": "result"}] - stream: Enable streaming. - - Returns: - Run: Updated run. - """ -``` - -Usage example: - -```python -# Check for required actions -run = client.beta.threads.runs.retrieve( - thread_id="thread_abc123", - run_id="run_abc123" -) - -if run.status == "requires_action": - tool_calls = run.required_action.submit_tool_outputs.tool_calls - - # Execute tool calls - tool_outputs = [] - for tool_call in tool_calls: - function_name = tool_call.function.name - arguments = tool_call.function.arguments - - # Call your function - result = execute_function(function_name, arguments) - - tool_outputs.append({ - "tool_call_id": tool_call.id, - "output": str(result) - }) - - # Submit outputs - run = client.beta.threads.runs.submit_tool_outputs( - thread_id="thread_abc123", - run_id="run_abc123", - tool_outputs=tool_outputs - ) -``` - -### Polling Helpers - -Wait for run completion with automatic polling. - -```python { .api } -def create_and_poll( - self, - thread_id: str, - *, - assistant_id: str, - poll_interval_ms: int = 1000, - **kwargs -) -> Run: - """Create run and poll until completion.""" - -def poll( - self, - thread_id: str, - run_id: str, - *, - poll_interval_ms: int = 1000, -) -> Run: - """Poll run until completion.""" - -def submit_tool_outputs_and_poll( - self, - thread_id: str, - run_id: str, - *, - tool_outputs: list[dict], - poll_interval_ms: int = 1000, -) -> Run: - """Submit tool outputs and poll until completion.""" -``` - -Usage examples: - -```python -# Create and wait for completion -run = client.beta.threads.runs.create_and_poll( - thread_id="thread_abc123", - assistant_id="asst_abc123" -) - -print(f"Final status: {run.status}") - -# Poll existing run -run = client.beta.threads.runs.poll( - thread_id="thread_abc123", - run_id="run_abc123" -) - -# Submit and wait -run = client.beta.threads.runs.submit_tool_outputs_and_poll( - thread_id="thread_abc123", - run_id="run_abc123", - tool_outputs=[{"tool_call_id": "call_abc", "output": "42"}] -) -``` - -### Streaming - -Stream run events in real-time. - -```python { .api } -def stream( - self, - thread_id: str, - *, - assistant_id: str, - **kwargs -) -> AssistantStreamManager: - """Stream run events with event handler.""" - -def create_and_stream( - self, - thread_id: str, - *, - assistant_id: str, - **kwargs -) -> AssistantStreamManager: - """Create run and stream events.""" -``` - -Usage example: - -```python -from openai import AssistantEventHandler - -# Define event handler -class EventHandler(AssistantEventHandler): - def on_text_created(self, text): - print(f"\\nassistant > ", end="", flush=True) - - def on_text_delta(self, delta, snapshot): - print(delta.value, end="", flush=True) - - def on_tool_call_created(self, tool_call): - print(f"\\nassistant > {tool_call.type}\\n", flush=True) - -# Stream events -with client.beta.threads.runs.stream( - thread_id="thread_abc123", - assistant_id="asst_abc123", - event_handler=EventHandler() -) as stream: - stream.until_done() -``` - -### Submit Tool Outputs with Streaming - -Submit tool outputs and stream the run to completion in real-time. - -```python { .api } -def submit_tool_outputs_stream( - self, - *, - tool_outputs: list[dict], - run_id: str, - thread_id: str, - event_handler: AssistantEventHandler | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> AssistantStreamManager[AssistantEventHandler]: - """ - Submit tool outputs and stream the run to terminal state. - - Helper method that submits tool call results and streams run events in real-time - until the run reaches a terminal state. More information on Run lifecycles: - https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps - - Args: - tool_outputs: List of tool call outputs. - [{"tool_call_id": "call_abc", "output": "result"}] - run_id: The run ID requiring tool outputs. - thread_id: The thread ID containing the run. - event_handler: Optional custom event handler for processing stream events. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - AssistantStreamManager: Stream manager for handling assistant events. - """ -``` - -Usage example: - -```python -from openai import AssistantEventHandler - -# Stream tool output submission -with client.beta.threads.runs.submit_tool_outputs_stream( - thread_id="thread_abc123", - run_id="run_abc123", - tool_outputs=[ - {"tool_call_id": "call_abc", "output": "42"}, - {"tool_call_id": "call_def", "output": "Hello"} - ] -) as stream: - for event in stream: - if event.event == 'thread.run.completed': - print("Run completed!") - -# With custom event handler -class ToolOutputHandler(AssistantEventHandler): - def on_text_delta(self, delta, snapshot): - print(delta.value, end='', flush=True) - - def on_run_step_done(self, run_step): - print(f"\nStep {run_step.id} completed") - -with client.beta.threads.runs.submit_tool_outputs_stream( - thread_id="thread_abc123", - run_id="run_abc123", - tool_outputs=[{"tool_call_id": "call_abc", "output": "result"}], - event_handler=ToolOutputHandler() -) as stream: - stream.until_done() -``` - -### Run Steps - -View detailed steps of a run. - -```python { .api } -def retrieve( - self, - thread_id: str, - run_id: str, - step_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> RunStep: - """Retrieve run step details.""" - -def list( - self, - thread_id: str, - run_id: str, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[RunStep]: - """List run steps.""" -``` - -Usage example: - -```python -# List steps -steps = client.beta.threads.runs.steps.list( - thread_id="thread_abc123", - run_id="run_abc123" -) - -for step in steps: - print(f"Step: {step.type}") - print(f"Status: {step.status}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Run(BaseModel): - """Assistant run.""" - id: str - assistant_id: str - cancelled_at: int | None - completed_at: int | None - created_at: int - expires_at: int - failed_at: int | None - incomplete_details: dict | None - instructions: str - last_error: dict | None - max_completion_tokens: int | None - max_prompt_tokens: int | None - metadata: dict[str, str] | None - model: str - object: Literal["thread.run"] - parallel_tool_calls: bool - required_action: RequiredAction | None - response_format: dict | None - started_at: int | None - status: Literal[ - "queued", "in_progress", "requires_action", - "cancelling", "cancelled", "failed", - "completed", "incomplete", "expired" - ] - thread_id: str - tool_choice: dict | str - tools: list[dict] - truncation_strategy: dict | None - usage: Usage | None - temperature: float | None - top_p: float | None - -class RequiredAction(BaseModel): - """Required action for run.""" - type: Literal["submit_tool_outputs"] - submit_tool_outputs: SubmitToolOutputs - -class SubmitToolOutputs(BaseModel): - """Tool outputs to submit.""" - tool_calls: list[ToolCall] - -class ToolCall(BaseModel): - """Tool call from assistant.""" - id: str - type: Literal["function"] - function: Function - -class Function(BaseModel): - """Function call details.""" - name: str - arguments: str # JSON string - -class Usage(BaseModel): - """Token usage.""" - completion_tokens: int - prompt_tokens: int - total_tokens: int - -class RunStep(BaseModel): - """Individual run step.""" - id: str - assistant_id: str - cancelled_at: int | None - completed_at: int | None - created_at: int - expired_at: int | None - failed_at: int | None - last_error: dict | None - metadata: dict[str, str] | None - object: Literal["thread.run.step"] - run_id: str - status: Literal[ - "in_progress", "cancelled", "failed", - "completed", "expired" - ] - step_details: StepDetails - thread_id: str - type: Literal["message_creation", "tool_calls"] - usage: Usage | None - -class StepDetails(BaseModel): - """Step details (message or tool calls).""" - type: Literal["message_creation", "tool_calls"] - message_creation: dict | None - tool_calls: list[dict] | None -``` - -## Complete Example with Tool Calls - -```python -from openai import OpenAI -import json - -client = OpenAI() - -# 1. Create assistant with function -assistant = client.beta.assistants.create( - name="Weather Assistant", - instructions="Help with weather queries.", - model="gpt-4", - tools=[{ - "type": "function", - "function": { - "name": "get_weather", - "description": "Get weather for a location", - "parameters": { - "type": "object", - "properties": { - "location": {"type": "string"} - }, - "required": ["location"] - } - } - }] -) - -# 2. Create thread and message -thread = client.beta.threads.create() - -client.beta.threads.messages.create( - thread_id=thread.id, - role="user", - content="What's the weather in San Francisco?" -) - -# 3. Run assistant -run = client.beta.threads.runs.create_and_poll( - thread_id=thread.id, - assistant_id=assistant.id -) - -# 4. Handle tool calls -if run.status == "requires_action": - tool_calls = run.required_action.submit_tool_outputs.tool_calls - - tool_outputs = [] - for tool_call in tool_calls: - if tool_call.function.name == "get_weather": - args = json.loads(tool_call.function.arguments) - # Call actual weather API - result = {"temp": 72, "condition": "sunny"} - - tool_outputs.append({ - "tool_call_id": tool_call.id, - "output": json.dumps(result) - }) - - # Submit and wait - run = client.beta.threads.runs.submit_tool_outputs_and_poll( - thread_id=thread.id, - run_id=run.id, - tool_outputs=tool_outputs - ) - -# 5. Get final response -messages = client.beta.threads.messages.list(thread_id=thread.id) -print(messages.data[0].content[0].text.value) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def run_assistant(): - client = AsyncOpenAI() - - run = await client.beta.threads.runs.create( - thread_id="thread_abc123", - assistant_id="asst_abc123" - ) - - while run.status not in ["completed", "failed"]: - await asyncio.sleep(1) - run = await client.beta.threads.runs.retrieve( - thread_id="thread_abc123", - run_id=run.id - ) - - return run - -run = asyncio.run(run_assistant()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/threads-messages.md b/.tessl/tiles/tessl/pypi-openai/docs/threads-messages.md deleted file mode 100644 index 0cd326b53..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/threads-messages.md +++ /dev/null @@ -1,679 +0,0 @@ -# Threads and Messages - -Create conversational threads and manage messages within the Assistants API. Threads maintain conversation state and messages represent user inputs and assistant responses. - -## Threads - -### Create Thread - -Create a new conversation thread. - -```python { .api } -def create( - self, - *, - messages: list[dict] | Omit = omit, - metadata: dict[str, str] | Omit = omit, - tool_resources: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Thread: - """ - Create a conversation thread. - - Args: - messages: Initial messages to add to thread. - [{"role": "user", "content": "Hello"}] - - metadata: Key-value pairs (max 16). Keys max 64 chars, values max 512 chars. - - tool_resources: Resources for tools. - - {"code_interpreter": {"file_ids": [...]}} - - {"file_search": {"vector_store_ids": [...]}} - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Thread: Created thread. - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create empty thread -thread = client.beta.threads.create() -print(f"Thread ID: {thread.id}") - -# Create with initial messages -thread = client.beta.threads.create( - messages=[ - {"role": "user", "content": "Hello!"}, - {"role": "user", "content": "How are you?"} - ] -) - -# With metadata -thread = client.beta.threads.create( - metadata={ - "user_id": "user-123", - "session": "abc" - } -) - -# With tool resources -thread = client.beta.threads.create( - tool_resources={ - "file_search": { - "vector_store_ids": ["vs_abc123"] - } - } -) -``` - -### Retrieve Thread - -Get thread details. - -```python { .api } -def retrieve( - self, - thread_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Thread: - """Get thread details.""" -``` - -### Update Thread - -Modify thread metadata. - -```python { .api } -def update( - self, - thread_id: str, - *, - metadata: dict[str, str] | Omit = omit, - tool_resources: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Thread: - """Update thread properties.""" -``` - -### Delete Thread - -Delete a thread. - -```python { .api } -def delete( - self, - thread_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> ThreadDeleted: - """Delete a thread.""" -``` - -### Create and Run - -Create thread and immediately run assistant (convenience method). - -```python { .api } -def create_and_run( - self, - *, - assistant_id: str, - instructions: str | Omit = omit, - metadata: dict[str, str] | Omit = omit, - model: str | Omit = omit, - thread: dict | Omit = omit, - tools: list[dict] | Omit = omit, - stream: bool | Omit = omit, - temperature: float | Omit = omit, - tool_choice: str | dict | Omit = omit, - top_p: float | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """ - Create thread and run assistant in one call. - - Args: - assistant_id: The assistant ID. - instructions: Override assistant instructions. - metadata: Thread metadata. - model: Override assistant model. - thread: Thread configuration including messages. - tools: Override assistant tools. - stream: Enable streaming. - temperature: Sampling temperature. - tool_choice: Tool choice configuration. - top_p: Nucleus sampling. - - Returns: - Run: Created run. - """ -``` - -Usage example: - -```python -# Create thread and run -run = client.beta.threads.create_and_run( - assistant_id="asst_abc123", - thread={ - "messages": [ - {"role": "user", "content": "Hello!"} - ] - } -) - -print(f"Run ID: {run.id}") -print(f"Thread ID: {run.thread_id}") -``` - -### Create and Run with Polling - -Create a thread, start a run, and automatically poll until the run reaches a terminal state. - -```python { .api } -def create_and_run_poll( - self, - *, - assistant_id: str, - instructions: str | Omit = omit, - max_completion_tokens: int | Omit = omit, - max_prompt_tokens: int | Omit = omit, - metadata: dict[str, str] | Omit = omit, - model: str | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - response_format: dict | Omit = omit, - temperature: float | Omit = omit, - thread: dict | Omit = omit, - tool_choice: str | dict | Omit = omit, - tool_resources: dict | Omit = omit, - tools: list[dict] | Omit = omit, - top_p: float | Omit = omit, - truncation_strategy: dict | Omit = omit, - poll_interval_ms: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Run: - """ - Create a thread, start a run, and poll for completion. - - Helper method that automatically polls the run until it reaches a terminal state - (completed, failed, cancelled, expired). More information on Run lifecycles: - https://platform.openai.com/docs/assistants/how-it-works/runs-and-run-steps - - Args: - assistant_id: The assistant ID to run. - poll_interval_ms: Polling interval in milliseconds (defaults to 1000ms). - thread: Thread configuration including messages. - instructions: Override assistant instructions. - tools: Override assistant tools. - metadata: Thread metadata. - (Additional parameters same as create_and_run) - - Returns: - Run: Completed run in terminal state. - """ -``` - -Usage example: - -```python -# Create and poll automatically -run = client.beta.threads.create_and_run_poll( - assistant_id="asst_abc123", - thread={ - "messages": [{"role": "user", "content": "Explain quantum computing"}] - }, - poll_interval_ms=500 # Poll every 500ms -) - -# Run is guaranteed to be in terminal state -print(f"Status: {run.status}") # completed, failed, cancelled, or expired -``` - -### Create and Run with Streaming - -Create a thread, start a run, and stream the response back in real-time. - -```python { .api } -def create_and_run_stream( - self, - *, - assistant_id: str, - instructions: str | Omit = omit, - max_completion_tokens: int | Omit = omit, - max_prompt_tokens: int | Omit = omit, - metadata: dict[str, str] | Omit = omit, - model: str | Omit = omit, - parallel_tool_calls: bool | Omit = omit, - response_format: dict | Omit = omit, - temperature: float | Omit = omit, - thread: dict | Omit = omit, - tool_choice: str | dict | Omit = omit, - tool_resources: dict | Omit = omit, - tools: list[dict] | Omit = omit, - top_p: float | Omit = omit, - truncation_strategy: dict | Omit = omit, - event_handler: AssistantEventHandler | None = None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> AssistantStreamManager[AssistantEventHandler]: - """ - Create a thread and stream the run back in real-time. - - Args: - assistant_id: The assistant ID to run. - event_handler: Optional custom event handler for processing stream events. - thread: Thread configuration including messages. - instructions: Override assistant instructions. - (Additional parameters same as create_and_run) - - Returns: - AssistantStreamManager: Stream manager for handling assistant events. - """ -``` - -Usage example: - -```python -# Stream with default handler -with client.beta.threads.create_and_run_stream( - assistant_id="asst_abc123", - thread={ - "messages": [{"role": "user", "content": "Tell me a story"}] - } -) as stream: - for event in stream: - if event.event == 'thread.message.delta': - print(event.data.delta.content[0].text.value, end='', flush=True) - -# With custom event handler -from openai import AssistantEventHandler - -class MyHandler(AssistantEventHandler): - def on_text_delta(self, delta, snapshot): - print(delta.value, end='', flush=True) - -with client.beta.threads.create_and_run_stream( - assistant_id="asst_abc123", - thread={ - "messages": [{"role": "user", "content": "Hello"}] - }, - event_handler=MyHandler() -) as stream: - stream.until_done() -``` - -## Messages - -### Create Message - -Add a message to a thread. - -```python { .api } -def create( - self, - thread_id: str, - *, - role: Literal["user", "assistant"], - content: str | list[dict], - attachments: list[dict] | Omit = omit, - metadata: dict[str, str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Message: - """ - Add a message to a thread. - - Args: - thread_id: The thread ID. - - role: Message role. "user" or "assistant". - - content: Message content. Can be: - - String: "Hello" - - List with text/images: [{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {...}}] - - attachments: File attachments with tools. - [{"file_id": "file-abc", "tools": [{"type": "code_interpreter"}]}] - - metadata: Key-value pairs. - - Returns: - Message: Created message. - """ -``` - -Usage examples: - -```python -# Simple text message -message = client.beta.threads.messages.create( - thread_id="thread_abc123", - role="user", - content="What is the weather today?" -) - -# With attachments -message = client.beta.threads.messages.create( - thread_id="thread_abc123", - role="user", - content="Analyze this data", - attachments=[ - { - "file_id": "file-abc123", - "tools": [{"type": "code_interpreter"}] - } - ] -) - -# Multimodal message (text + image) -message = client.beta.threads.messages.create( - thread_id="thread_abc123", - role="user", - content=[ - {"type": "text", "text": "What's in this image?"}, - { - "type": "image_url", - "image_url": {"url": "https://example.com/image.jpg"} - } - ] -) - -# With metadata -message = client.beta.threads.messages.create( - thread_id="thread_abc123", - role="user", - content="Question here", - metadata={"source": "web_ui"} -) -``` - -### Retrieve Message - -Get message details. - -```python { .api } -def retrieve( - self, - thread_id: str, - message_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Message: - """Get message details.""" -``` - -### Update Message - -Modify message metadata. - -```python { .api } -def update( - self, - thread_id: str, - message_id: str, - *, - metadata: dict[str, str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Message: - """Update message metadata.""" -``` - -### List Messages - -List messages in a thread. - -```python { .api } -def list( - self, - thread_id: str, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - run_id: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[Message]: - """ - List messages in a thread. - - Args: - thread_id: The thread ID. - after: Cursor for next page. - before: Cursor for previous page. - limit: Number to retrieve (max 100). Default 20. - order: Sort order. "asc" or "desc". Default "desc" (newest first). - run_id: Filter by run ID. - - Returns: - SyncCursorPage[Message]: Paginated messages. - """ -``` - -Usage example: - -```python -# List all messages -messages = client.beta.threads.messages.list(thread_id="thread_abc123") - -for message in messages: - print(f"{message.role}: {message.content[0].text.value}") - -# List in chronological order -messages = client.beta.threads.messages.list( - thread_id="thread_abc123", - order="asc" -) - -# Pagination -page1 = client.beta.threads.messages.list(thread_id="thread_abc123", limit=10) -page2 = client.beta.threads.messages.list( - thread_id="thread_abc123", - limit=10, - after=page1.data[-1].id -) -``` - -### Delete Message - -Delete a message. - -```python { .api } -def delete( - self, - thread_id: str, - message_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> MessageDeleted: - """Delete a message.""" -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Thread(BaseModel): - """Conversation thread.""" - id: str - created_at: int - metadata: dict[str, str] | None - object: Literal["thread"] - tool_resources: dict | None - -class ThreadDeleted(BaseModel): - """Deletion confirmation.""" - id: str - deleted: bool - object: Literal["thread.deleted"] - -class Message(BaseModel): - """Thread message.""" - id: str - assistant_id: str | None - attachments: list[dict] | None - completed_at: int | None - content: list[MessageContent] - created_at: int - incomplete_at: int | None - incomplete_details: dict | None - metadata: dict[str, str] | None - object: Literal["thread.message"] - role: Literal["user", "assistant"] - run_id: str | None - status: Literal["in_progress", "incomplete", "completed"] - thread_id: str - -class MessageContent(BaseModel): - """Message content part.""" - type: Literal["text", "image_file", "image_url"] - text: TextContent | None - image_file: ImageFile | None - image_url: ImageURL | None - -class TextContent(BaseModel): - """Text content.""" - value: str - annotations: list[Annotation] - -class ImageFile(BaseModel): - """Image file reference.""" - file_id: str - detail: Literal["auto", "low", "high"] | None - -class ImageURL(BaseModel): - """Image URL reference.""" - url: str - detail: Literal["auto", "low", "high"] | None - -class Annotation(BaseModel): - """Content annotation (file citation/path).""" - type: Literal["file_citation", "file_path"] - text: str - start_index: int - end_index: int - file_citation: dict | None - file_path: dict | None - -class MessageDeleted(BaseModel): - """Deletion confirmation.""" - id: str - deleted: bool - object: Literal["thread.message.deleted"] -``` - -## Complete Example - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Create assistant -assistant = client.beta.assistants.create( - name="Helper", - model="gpt-4", - instructions="You are helpful." -) - -# 2. Create thread -thread = client.beta.threads.create() - -# 3. Add user message -message = client.beta.threads.messages.create( - thread_id=thread.id, - role="user", - content="Tell me a joke" -) - -# 4. Run assistant -run = client.beta.threads.runs.create( - thread_id=thread.id, - assistant_id=assistant.id -) - -# 5. Wait for completion -import time - -while run.status not in ["completed", "failed"]: - time.sleep(1) - run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id) - -# 6. Get messages -messages = client.beta.threads.messages.list(thread_id=thread.id) - -for message in messages: - role = message.role - content = message.content[0].text.value - print(f"{role}: {content}") -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_conversation(): - client = AsyncOpenAI() - - thread = await client.beta.threads.create() - - message = await client.beta.threads.messages.create( - thread_id=thread.id, - role="user", - content="Hello!" - ) - - return thread.id, message.id - -thread_id, message_id = asyncio.run(create_conversation()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/uploads.md b/.tessl/tiles/tessl/pypi-openai/docs/uploads.md deleted file mode 100644 index 29518a950..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/uploads.md +++ /dev/null @@ -1,466 +0,0 @@ -# Uploads - -Upload large files in chunks for use with Assistants, Fine-tuning, and Batch processing. The Uploads API enables efficient multipart upload of files up to 8 GB, splitting them into 64 MB parts that can be uploaded in parallel. - -## Capabilities - -### Create Upload - -Create an intermediate Upload object that accepts multiple parts. - -```python { .api } -def create( - self, - *, - bytes: int, - filename: str, - mime_type: str, - purpose: FilePurpose, - expires_after: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Upload: - """ - Create an intermediate Upload object for adding file parts. - - Args: - bytes: Total number of bytes in the file being uploaded. - - filename: Name of the file to upload. - - mime_type: MIME type of the file. Must be supported for the specified purpose. - See https://platform.openai.com/docs/assistants/tools/file-search#supported-files - - purpose: Intended purpose of the file. Options: - - "assistants": For use with Assistants API - - "batch": For batch processing - - "fine-tune": For fine-tuning - - "vision": For vision capabilities - See https://platform.openai.com/docs/api-reference/files/create#files-create-purpose - - expires_after: Expiration policy for the file. Default: files with purpose=batch - expire after 30 days, others persist until manually deleted. - {"anchor": "created_at", "days": 7} expires 7 days after creation. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Upload: Upload object with ID to use for adding parts. - Contains status, expires_at, and other metadata. - - Notes: - - Maximum upload size: 8 GB - - Upload expires 1 hour after creation - - Must complete upload before expiration - - Each part can be at most 64 MB - """ -``` - -### Complete Upload - -Finalize the upload after all parts have been added. - -```python { .api } -def complete( - self, - upload_id: str, - *, - part_ids: list[str], - md5: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Upload: - """ - Complete the Upload and create a File object. - - Args: - upload_id: ID of the Upload to complete. - - part_ids: Ordered list of Part IDs. Order determines how parts are assembled. - - md5: Optional MD5 checksum to verify uploaded bytes match expectations. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Upload: Completed Upload object containing a nested File object - ready for use in the rest of the platform. - - Notes: - - Total bytes uploaded must match bytes specified in create() - - No parts can be added after completion - - Upload must not be cancelled or expired - """ -``` - -### Cancel Upload - -Cancel an upload that is no longer needed. - -```python { .api } -def cancel( - self, - upload_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Upload: - """ - Cancel an Upload. - - Args: - upload_id: ID of the Upload to cancel. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Upload: Cancelled Upload object with status="cancelled". - - Notes: - - No parts can be added after cancellation - - Previously uploaded parts are discarded - """ -``` - -### Upload File Chunked - -High-level helper that handles the entire upload process automatically. - -```python { .api } -def upload_file_chunked( - self, - *, - file: str | os.PathLike | bytes, - mime_type: str, - purpose: FilePurpose, - filename: str | None = None, - bytes: int | None = None, - part_size: int | None = None, - md5: str | Omit = omit, -) -> Upload: - """ - Upload a file in chunks automatically. - - This convenience method handles: - 1. Creating the Upload - 2. Splitting file into parts - 3. Uploading each part sequentially - 4. Completing the Upload - - Args: - file: File to upload. Can be: - - Path-like object: Path("my-paper.pdf") - - String path: "my-paper.pdf" - - bytes: In-memory file data (requires filename and bytes args) - - mime_type: MIME type of the file (e.g., "application/pdf"). - - purpose: Intended purpose ("assistants", "batch", "fine-tune", "vision"). - - filename: Filename (required if file is bytes, optional otherwise). - - bytes: Total file size in bytes (required if file is bytes, optional otherwise). - If not provided for path, automatically determined from file. - - part_size: Size of each part in bytes. Default: 64 MB (64 * 1024 * 1024). - Each part uploads as a separate request. - - md5: Optional MD5 checksum for verification. - - Returns: - Upload: Completed Upload object containing the File. - - Raises: - TypeError: If filename or bytes not provided for in-memory files. - ValueError: If file path is invalid or file cannot be read. - """ -``` - -Usage examples: - -```python -from pathlib import Path -from openai import OpenAI - -client = OpenAI() - -# Upload a file from disk (simplest approach) -upload = client.uploads.upload_file_chunked( - file=Path("training_data.jsonl"), - mime_type="application/jsonl", - purpose="fine-tune" -) - -print(f"Upload complete! File ID: {upload.file.id}") - -# Upload with custom part size (e.g., 32 MB parts) -upload = client.uploads.upload_file_chunked( - file="large_dataset.jsonl", - mime_type="application/jsonl", - purpose="batch", - part_size=32 * 1024 * 1024 -) - -# Upload in-memory bytes -file_data = b"..." # Your file data -upload = client.uploads.upload_file_chunked( - file=file_data, - filename="document.pdf", - bytes=len(file_data), - mime_type="application/pdf", - purpose="assistants" -) - -# Upload with MD5 verification -upload = client.uploads.upload_file_chunked( - file="important_data.csv", - mime_type="text/csv", - purpose="assistants", - md5="5d41402abc4b2a76b9719d911017c592" -) -``` - -### Create Part - -Add a single part to an Upload. - -```python { .api } -def create( - self, - upload_id: str, - *, - data: FileTypes, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> UploadPart: - """ - Add a Part to an Upload. - - Args: - upload_id: ID of the Upload to add this Part to. - - data: Chunk of bytes for this Part. Maximum 64 MB. - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - UploadPart: Part object with ID to use when completing the Upload. - - Notes: - - Each Part can be at most 64 MB - - Total size across all parts cannot exceed 8 GB - - Parts can be added in parallel for faster uploads - - Order is determined when completing the Upload - """ -``` - -Advanced manual upload example: - -```python -from openai import OpenAI -import io - -client = OpenAI() - -# Step 1: Create the Upload -file_path = "large_file.pdf" -file_size = os.path.getsize(file_path) - -upload = client.uploads.create( - bytes=file_size, - filename="large_file.pdf", - mime_type="application/pdf", - purpose="assistants" -) - -print(f"Created upload: {upload.id}") - -# Step 2: Upload parts -part_size = 64 * 1024 * 1024 # 64 MB -part_ids = [] - -with open(file_path, "rb") as f: - while True: - chunk = f.read(part_size) - if not chunk: - break - - part = client.uploads.parts.create( - upload_id=upload.id, - data=chunk - ) - part_ids.append(part.id) - print(f"Uploaded part {len(part_ids)}: {part.id}") - -# Step 3: Complete the Upload -completed = client.uploads.complete( - upload_id=upload.id, - part_ids=part_ids -) - -print(f"Upload complete! File ID: {completed.file.id}") - -# Handle errors by cancelling -try: - # ... upload process ... - pass -except Exception as e: - print(f"Error during upload: {e}") - client.uploads.cancel(upload_id=upload.id) - print("Upload cancelled") -``` - -Parallel upload example: - -```python -from openai import OpenAI -from concurrent.futures import ThreadPoolExecutor -import io - -client = OpenAI() - -def upload_part(upload_id: str, part_data: bytes) -> str: - """Upload a single part and return its ID.""" - part = client.uploads.parts.create( - upload_id=upload_id, - data=part_data - ) - return part.id - -# Create upload -file_path = "large_file.pdf" -file_size = os.path.getsize(file_path) - -upload = client.uploads.create( - bytes=file_size, - filename="large_file.pdf", - mime_type="application/pdf", - purpose="assistants" -) - -# Split file into chunks -part_size = 64 * 1024 * 1024 -chunks = [] - -with open(file_path, "rb") as f: - while True: - chunk = f.read(part_size) - if not chunk: - break - chunks.append(chunk) - -# Upload parts in parallel -with ThreadPoolExecutor(max_workers=4) as executor: - part_ids = list(executor.map( - lambda chunk: upload_part(upload.id, chunk), - chunks - )) - -# Complete upload -completed = client.uploads.complete( - upload_id=upload.id, - part_ids=part_ids -) - -print(f"Parallel upload complete! File ID: {completed.file.id}") -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def upload_file(): - client = AsyncOpenAI() - - # Async upload - upload = await client.uploads.upload_file_chunked( - file="data.jsonl", - mime_type="application/jsonl", - purpose="fine-tune" - ) - - return upload.file.id - -file_id = asyncio.run(upload_file()) -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Upload(BaseModel): - """Upload object containing metadata and status.""" - id: str - bytes: int - created_at: int - expires_at: int - filename: str - object: Literal["upload"] - purpose: FilePurpose - status: Literal["pending", "completed", "cancelled", "expired"] - file: FileObject | None # Present when status="completed" - -class UploadPart(BaseModel): - """Part object representing a chunk of an upload.""" - id: str - created_at: int - object: Literal["upload.part"] - upload_id: str - -FilePurpose = Literal["assistants", "batch", "fine-tune", "vision"] - -FileTypes = Union[ - FileContent, - Tuple[Optional[str], FileContent], - Tuple[Optional[str], FileContent, Optional[str]] -] - -class Omit: - """Sentinel value for omitted parameters.""" -``` - -## Access Pattern - -```python -# Synchronous -from openai import OpenAI -client = OpenAI() -client.uploads.create(...) -client.uploads.complete(...) -client.uploads.cancel(...) -client.uploads.upload_file_chunked(...) -client.uploads.parts.create(...) - -# Asynchronous -from openai import AsyncOpenAI -client = AsyncOpenAI() -await client.uploads.create(...) -await client.uploads.complete(...) -await client.uploads.cancel(...) -await client.uploads.upload_file_chunked(...) -await client.uploads.parts.create(...) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/vector-stores.md b/.tessl/tiles/tessl/pypi-openai/docs/vector-stores.md deleted file mode 100644 index e45582cc9..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/vector-stores.md +++ /dev/null @@ -1,1146 +0,0 @@ -# Vector Stores - -Create and manage vector stores for semantic search and retrieval with the Assistants API. Vector stores enable file search capabilities by storing and indexing documents for efficient retrieval. - -## Capabilities - -### Create Vector Store - -Create a new vector store for storing and searching documents. - -```python { .api } -def create( - self, - *, - chunking_strategy: dict | Omit = omit, - description: str | Omit = omit, - expires_after: dict | Omit = omit, - file_ids: list[str] | Omit = omit, - metadata: dict[str, str] | Omit = omit, - name: str | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStore: - """ - Create a vector store for file search. - - Args: - chunking_strategy: How to chunk files. Options: - - {"type": "auto"}: Automatic chunking (default) - - {"type": "static", "static": {"max_chunk_size_tokens": 800, "chunk_overlap_tokens": 400}} - - description: Description of the vector store (optional). - - expires_after: Expiration policy. Options: - - {"anchor": "last_active_at", "days": 7}: Expires 7 days after last use - - {"anchor": "last_active_at", "days": 1}: Expires 1 day after last use - - file_ids: List of file IDs to add to the vector store (max 10000). - Files must have purpose="assistants". - - metadata: Key-value pairs for storing additional info (max 16). - Keys max 64 chars, values max 512 chars. - - name: Name of the vector store (optional). - - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStore: Created vector store. - - Raises: - BadRequestError: Invalid parameters or too many files - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create empty vector store -vector_store = client.beta.vector_stores.create( - name="Product Documentation" -) - -print(f"Vector Store ID: {vector_store.id}") - -# Create with files -file_ids = ["file-abc123", "file-def456"] - -vector_store = client.beta.vector_stores.create( - name="Knowledge Base", - file_ids=file_ids -) - -# With expiration policy -vector_store = client.beta.vector_stores.create( - name="Temporary Store", - expires_after={"anchor": "last_active_at", "days": 7} -) - -# With custom chunking -vector_store = client.beta.vector_stores.create( - name="Custom Chunking", - file_ids=file_ids, - chunking_strategy={ - "type": "static", - "static": { - "max_chunk_size_tokens": 800, - "chunk_overlap_tokens": 400 - } - } -) - -# With metadata -vector_store = client.beta.vector_stores.create( - name="Project Docs", - metadata={ - "project": "alpha", - "version": "1.0" - } -) -``` - -### Retrieve Vector Store - -Get vector store details. - -```python { .api } -def retrieve( - self, - vector_store_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStore: - """ - Retrieve vector store details. - - Args: - vector_store_id: The ID of the vector store. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStore: Vector store details. - - Raises: - NotFoundError: Vector store not found - """ -``` - -Usage example: - -```python -# Get vector store -store = client.beta.vector_stores.retrieve("vs_abc123") - -print(f"Name: {store.name}") -print(f"File counts: {store.file_counts}") -print(f"Status: {store.status}") -``` - -### Update Vector Store - -Modify vector store settings. - -```python { .api } -def update( - self, - vector_store_id: str, - *, - name: str | Omit = omit, - expires_after: dict | Omit = omit, - metadata: dict[str, str] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStore: - """ - Update vector store properties. - - Args: - vector_store_id: The ID of the vector store. - name: New name for the vector store. - expires_after: New expiration policy. - metadata: New metadata (replaces existing). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStore: Updated vector store. - """ -``` - -Usage example: - -```python -# Update name -store = client.beta.vector_stores.update( - "vs_abc123", - name="Updated Documentation" -) - -# Update metadata -store = client.beta.vector_stores.update( - "vs_abc123", - metadata={"version": "2.0"} -) -``` - -### List Vector Stores - -List all vector stores with pagination. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - before: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[VectorStore]: - """ - List vector stores with pagination. - - Args: - after: Cursor for next page. - before: Cursor for previous page. - limit: Number to retrieve (max 100). Default 20. - order: Sort order. "asc" or "desc". Default "desc". - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[VectorStore]: Paginated list of vector stores. - """ -``` - -Usage example: - -```python -# List all stores -stores = client.beta.vector_stores.list() - -for store in stores: - print(f"{store.name} ({store.id})") - -# Pagination -page1 = client.beta.vector_stores.list(limit=10) -page2 = client.beta.vector_stores.list(limit=10, after=page1.data[-1].id) -``` - -### Delete Vector Store - -Delete a vector store and all its files. - -```python { .api } -def delete( - self, - vector_store_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreDeleted: - """ - Delete a vector store. - - Args: - vector_store_id: The ID of the vector store to delete. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreDeleted: Deletion confirmation. - - Raises: - NotFoundError: Vector store not found - """ -``` - -Usage example: - -```python -# Delete vector store -result = client.beta.vector_stores.delete("vs_abc123") - -print(f"Deleted: {result.deleted}") -``` - -### Add Files to Vector Store - -Add files to an existing vector store. - -```python { .api } -def create( - self, - vector_store_id: str, - *, - file_id: str, - attributes: dict[str, str | float | bool] | None | Omit = omit, - chunking_strategy: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFile: - """ - Add a file to a vector store. - - Args: - vector_store_id: The vector store ID. - file_id: The file ID to add. - attributes: Key-value pairs that can be attached to the file (max 16 pairs). - Keys: max 64 chars. Values: max 512 chars (strings) or numbers/booleans. - Useful for storing metadata like version numbers, categories, etc. - chunking_strategy: Chunking configuration (same as vector store create). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFile: Added file details. - """ -``` - -Usage example: - -```python -# Add file to vector store -file = client.beta.vector_stores.files.create( - vector_store_id="vs_abc123", - file_id="file-xyz789" -) - -print(f"File status: {file.status}") -``` - -### Retrieve Vector Store File - -Get details about a file in a vector store. - -```python { .api } -def retrieve( - self, - file_id: str, - *, - vector_store_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFile: - """ - Retrieve details about a vector store file. - - Args: - file_id: The ID of the file. - vector_store_id: The vector store ID. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFile: File details including status and metadata. - - Raises: - NotFoundError: File not found in vector store - """ -``` - -Usage example: - -```python -# Get file details -file = client.beta.vector_stores.files.retrieve( - file_id="file-xyz789", - vector_store_id="vs_abc123" -) - -print(f"Status: {file.status}") -print(f"Usage bytes: {file.usage_bytes}") -``` - -### Update Vector Store File - -Update attributes on a vector store file. - -```python { .api } -def update( - self, - file_id: str, - *, - vector_store_id: str, - attributes: dict[str, str | float | bool] | None, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFile: - """ - Update attributes on a vector store file. - - Args: - file_id: The ID of the file. - vector_store_id: The vector store ID. - attributes: Key-value pairs to attach (max 16 pairs). - Keys: max 64 chars, Values: max 512 chars (or numbers/booleans). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFile: Updated file object. - """ -``` - -Usage example: - -```python -# Update file attributes -file = client.beta.vector_stores.files.update( - file_id="file-xyz789", - vector_store_id="vs_abc123", - attributes={ - "category": "documentation", - "version": "1.2.0", - "priority": 5 - } -) -``` - -### List Vector Store Files - -List all files in a vector store with pagination and filtering. - -```python { .api } -def list( - self, - vector_store_id: str, - *, - after: str | Omit = omit, - before: str | Omit = omit, - filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[VectorStoreFile]: - """ - List files in a vector store with optional filtering. - - Args: - vector_store_id: The vector store ID. - after: Cursor for pagination (object ID to start after). - before: Cursor for pagination (object ID to start before). - filter: Filter by file status: "in_progress", "completed", "failed", "cancelled". - limit: Number of files to return (1-100, default 20). - order: Sort order by created_at: "asc" or "desc". - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[VectorStoreFile]: Paginated list of files. - """ -``` - -Usage examples: - -```python -# List all files -files = client.beta.vector_stores.files.list( - vector_store_id="vs_abc123" -) - -for file in files: - print(f"{file.id}: {file.status}") - -# Filter by status -completed_files = client.beta.vector_stores.files.list( - vector_store_id="vs_abc123", - filter="completed" -) - -# Pagination -page1 = client.beta.vector_stores.files.list( - vector_store_id="vs_abc123", - limit=10, - order="desc" -) - -page2 = client.beta.vector_stores.files.list( - vector_store_id="vs_abc123", - limit=10, - after=page1.data[-1].id -) -``` - -### Delete Vector Store File - -Remove a file from a vector store (does not delete the file itself). - -```python { .api } -def delete( - self, - file_id: str, - *, - vector_store_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFileDeleted: - """ - Delete a vector store file. - - This removes the file from the vector store but does not delete the file - itself. To delete the file, use client.files.delete(). - - Args: - file_id: The ID of the file. - vector_store_id: The vector store ID. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFileDeleted: Deletion confirmation. - """ -``` - -Usage example: - -```python -# Remove file from vector store -deleted = client.beta.vector_stores.files.delete( - file_id="file-xyz789", - vector_store_id="vs_abc123" -) - -print(f"Deleted: {deleted.id}") - -# To also delete the file itself: -client.files.delete(file_id="file-xyz789") -``` - -### Get Vector Store File Content - -Retrieve the parsed contents of a vector store file. - -```python { .api } -def content( - self, - file_id: str, - *, - vector_store_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncPage[FileContentResponse]: - """ - Retrieve the parsed contents of a vector store file. - - Args: - file_id: The ID of the file. - vector_store_id: The vector store ID. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncPage[FileContentResponse]: Parsed file content with chunks. - """ -``` - -Usage example: - -```python -# Get parsed file content -content_pages = client.beta.vector_stores.files.content( - file_id="file-xyz789", - vector_store_id="vs_abc123" -) - -for page in content_pages: - print(f"Content: {page.content}") - print(f"Metadata: {page.metadata}") -``` - -### Helper: Create and Poll - -Convenience method that combines create() and poll() - adds a file and waits for processing. - -```python { .api } -def create_and_poll( - self, - file_id: str, - *, - vector_store_id: str, - attributes: dict[str, str | float | bool] | None | Omit = omit, - poll_interval_ms: int | Omit = omit, - chunking_strategy: dict | Omit = omit, -) -> VectorStoreFile: - """ - Attach a file to the given vector store and wait for it to be processed. - - Args: - file_id: The file ID to add. - vector_store_id: The vector store ID. - attributes: Key-value pairs to attach to the file. - poll_interval_ms: Polling interval in milliseconds. If not specified, uses server-suggested interval. - chunking_strategy: Chunking configuration. - - Returns: - VectorStoreFile: Processed file details (may be completed or failed). - """ -``` - -Usage example: - -```python -# Add file and wait for processing -file = client.beta.vector_stores.files.create_and_poll( - file_id="file-xyz789", - vector_store_id="vs_abc123", - poll_interval_ms=1000 -) - -print(f"Final status: {file.status}") -if file.status == "failed": - print(f"Error: {file.last_error}") -``` - -### Helper: Poll Processing - -Wait for a vector store file to finish processing. - -```python { .api } -def poll( - self, - file_id: str, - *, - vector_store_id: str, - poll_interval_ms: int | Omit = omit, -) -> VectorStoreFile: - """ - Wait for the vector store file to finish processing. - - Note: this will return even if the file failed to process. Check - file.status and file.last_error to handle failures. - - Args: - file_id: The file ID. - vector_store_id: The vector store ID. - poll_interval_ms: Polling interval in milliseconds. If not specified, uses server-suggested interval. - - Returns: - VectorStoreFile: File details after processing completes (or fails). - """ -``` - -Usage example: - -```python -# First create the file -file = client.beta.vector_stores.files.create( - file_id="file-xyz789", - vector_store_id="vs_abc123" -) - -# Then poll until processing completes -processed_file = client.beta.vector_stores.files.poll( - file_id="file-xyz789", - vector_store_id="vs_abc123" -) - -print(f"Status: {processed_file.status}") -``` - -### Helper: Upload and Attach - -Upload a new file to the Files API and attach it to the vector store. - -```python { .api } -def upload( - self, - *, - vector_store_id: str, - file: FileTypes, - chunking_strategy: dict | Omit = omit, -) -> VectorStoreFile: - """ - Upload a file to the Files API and attach it to the given vector store. - - Note: The file will be asynchronously processed. Use upload_and_poll() - to wait for processing to complete. - - Args: - vector_store_id: The vector store ID. - file: File to upload (path, file object, or bytes). - chunking_strategy: Chunking configuration. - - Returns: - VectorStoreFile: File details (status will be "in_progress"). - """ -``` - -Usage example: - -```python -# Upload and attach file -with open("document.pdf", "rb") as f: - file = client.beta.vector_stores.files.upload( - vector_store_id="vs_abc123", - file=f - ) - -print(f"Uploaded file ID: {file.id}") -print(f"Status: {file.status}") -``` - -### Helper: Upload and Poll - -Complete workflow - upload a file, attach to vector store, and wait for processing. - -```python { .api } -def upload_and_poll( - self, - *, - vector_store_id: str, - file: FileTypes, - attributes: dict[str, str | float | bool] | None | Omit = omit, - poll_interval_ms: int | Omit = omit, - chunking_strategy: dict | Omit = omit, -) -> VectorStoreFile: - """ - Upload a file and poll until processing is complete. - - This is the most convenient method for adding files - it handles - the upload, attachment, and waiting in one call. - - Args: - vector_store_id: The vector store ID. - file: File to upload (path, file object, or bytes). - attributes: Key-value pairs to attach to the file. - poll_interval_ms: Polling interval in milliseconds. - chunking_strategy: Chunking configuration. - - Returns: - VectorStoreFile: Processed file details (may be completed or failed). - """ -``` - -Usage example: - -```python -# Complete workflow in one call -with open("document.pdf", "rb") as f: - file = client.beta.vector_stores.files.upload_and_poll( - vector_store_id="vs_abc123", - file=f, - attributes={"type": "documentation", "version": "2.0"}, - poll_interval_ms=1000 - ) - -print(f"File ID: {file.id}") -print(f"Status: {file.status}") -print(f"Usage bytes: {file.usage_bytes}") - -if file.status == "failed": - print(f"Error: {file.last_error}") -``` - -### Search Vector Store - -Search for relevant content in a vector store based on a query and optional file attributes filter. - -```python { .api } -def search( - self, - vector_store_id: str, - *, - query: str | list[str], - filters: dict | Omit = omit, - max_num_results: int | Omit = omit, - ranking_options: dict | Omit = omit, - rewrite_query: bool | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncPage[VectorStoreSearchResponse]: - """ - Search vector store for relevant content. - - Args: - vector_store_id: The vector store ID. - query: Search query text (string or list of strings). - filters: A filter to apply based on file attributes. - max_num_results: Maximum number of results to return (1-50 inclusive). - ranking_options: Ranking options for search. - rewrite_query: Whether to rewrite the natural language query for vector search. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncPage[VectorStoreSearchResponse]: Paginated search results with relevant chunks. - """ -``` - -Usage example: - -```python -# Search vector store -results = client.beta.vector_stores.search( - vector_store_id="vs_abc123", - query="How do I install the SDK?", - max_num_results=5 -) - -for result in results.data: - print(f"Score: {result.score}") - print(f"Content: {result.content}") - print(f"File: {result.file_id}") -``` - -### File Batches - -Batch operations for adding multiple files to a vector store efficiently. Accessed via `client.beta.vector_stores.file_batches`. - -```python { .api } -def create( - self, - vector_store_id: str, - *, - file_ids: list[str] | Omit = omit, - files: list[dict] | Omit = omit, - attributes: dict[str, str | float | bool] | None | Omit = omit, - chunking_strategy: dict | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFileBatch: - """ - Create a batch of files to add to vector store. - - Args: - vector_store_id: The vector store ID. - file_ids: List of file IDs to add (mutually exclusive with files). - files: List of file objects with per-file metadata (mutually exclusive with file_ids). - attributes: Metadata to apply to all files in batch. - chunking_strategy: Strategy for chunking files. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFileBatch: Created batch object. - """ - -def retrieve( - self, - batch_id: str, - *, - vector_store_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFileBatch: - """ - Retrieve file batch status. - - Args: - batch_id: The file batch ID. - vector_store_id: The vector store ID. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFileBatch: Batch details. - """ - -def cancel( - self, - batch_id: str, - *, - vector_store_id: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VectorStoreFileBatch: - """ - Cancel an in-progress file batch. - - Args: - batch_id: The file batch ID. - vector_store_id: The vector store ID. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - VectorStoreFileBatch: Updated batch with cancelled status. - """ - -def list_files( - self, - batch_id: str, - *, - vector_store_id: str, - after: str | Omit = omit, - before: str | Omit = omit, - filter: Literal["in_progress", "completed", "failed", "cancelled"] | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncCursorPage[VectorStoreFile]: - """ - List files in a vector store batch with pagination and filtering. - - Args: - batch_id: The file batch ID to list files from. - vector_store_id: The vector store ID. - after: Cursor for pagination. Return files after this file ID. - before: Cursor for pagination. Return files before this file ID. - filter: Filter by file status. Options: - - "in_progress": Files currently processing - - "completed": Successfully processed files - - "failed": Files that failed processing - - "cancelled": Cancelled files - limit: Number of files to retrieve. Default 20, max 100. - order: Sort order. "asc" for ascending, "desc" for descending. Default "desc". - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncCursorPage[VectorStoreFile]: Paginated list of files in the batch. - """ -``` - -Usage examples: - -```python -# Create file batch -batch = client.beta.vector_stores.file_batches.create( - vector_store_id="vs_abc123", - file_ids=["file-1", "file-2", "file-3"] -) - -print(f"Batch ID: {batch.id}") -print(f"Status: {batch.status}") - -# Check batch status -batch = client.beta.vector_stores.file_batches.retrieve( - batch_id=batch.id, - vector_store_id="vs_abc123" -) - -# List files in batch -files = client.beta.vector_stores.file_batches.list_files( - batch_id=batch.id, - vector_store_id="vs_abc123" -) - -for file in files.data: - print(f"File ID: {file.id}, Status: {file.status}") - -# Filter by status -completed_files = client.beta.vector_stores.file_batches.list_files( - batch_id=batch.id, - vector_store_id="vs_abc123", - filter="completed" -) - -# Cancel batch if needed -batch = client.beta.vector_stores.file_batches.cancel( - batch_id=batch.id, - vector_store_id="vs_abc123" -) -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class VectorStore(BaseModel): - """Vector store for file search.""" - id: str - created_at: int - name: str - usage_bytes: int - file_counts: FileCounts - status: Literal["expired", "in_progress", "completed"] - expires_after: dict | None - expires_at: int | None - last_active_at: int | None - metadata: dict[str, str] | None - object: Literal["vector_store"] - -class FileCounts(BaseModel): - """File count statistics.""" - in_progress: int - completed: int - failed: int - cancelled: int - total: int - -class VectorStoreDeleted(BaseModel): - """Deletion confirmation.""" - id: str - deleted: bool - object: Literal["vector_store.deleted"] - -class VectorStoreFile(BaseModel): - """File in vector store.""" - id: str - created_at: int - vector_store_id: str - usage_bytes: int - status: Literal["in_progress", "completed", "cancelled", "failed"] - last_error: dict | None - chunking_strategy: dict | None - object: Literal["vector_store.file"] - -class VectorStoreFileBatch(BaseModel): - """Batch of files being added to vector store.""" - id: str - created_at: int - vector_store_id: str - status: Literal["in_progress", "completed", "cancelled", "failed"] - file_counts: FileCounts - object: Literal["vector_store.files_batch"] - -class VectorStoreSearchResponse(BaseModel): - """Search results.""" - data: list[SearchResult] - object: str - -class SearchResult(BaseModel): - """Single search result.""" - content: str - file_id: str - score: float - metadata: dict | None -``` - -## Best Practices - -```python -from openai import OpenAI - -client = OpenAI() - -# 1. Create vector store with appropriate files -# Upload files first -file_ids = [] -for doc_path in ["doc1.pdf", "doc2.txt", "doc3.md"]: - with open(doc_path, "rb") as f: - file = client.files.create(file=f, purpose="assistants") - file_ids.append(file.id) - -# Create vector store -store = client.beta.vector_stores.create( - name="Product Documentation", - file_ids=file_ids -) - -# 2. Wait for processing -import time - -while store.status == "in_progress": - time.sleep(2) - store = client.beta.vector_stores.retrieve(store.id) - -print(f"Status: {store.status}") -print(f"Completed files: {store.file_counts.completed}") - -# 3. Use with Assistant -assistant = client.beta.assistants.create( - name="Documentation Assistant", - instructions="Help users find information in documentation.", - model="gpt-4", - tools=[{"type": "file_search"}], - tool_resources={ - "file_search": { - "vector_store_ids": [store.id] - } - } -) - -# 4. Clean up expired stores -stores = client.beta.vector_stores.list() -for store in stores: - if store.status == "expired": - client.beta.vector_stores.delete(store.id) -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_store(): - client = AsyncOpenAI() - - store = await client.beta.vector_stores.create( - name="Async Store", - file_ids=["file-abc123"] - ) - - return store.id - -store_id = asyncio.run(create_store()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/videos.md b/.tessl/tiles/tessl/pypi-openai/docs/videos.md deleted file mode 100644 index bb017aaaa..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/videos.md +++ /dev/null @@ -1,361 +0,0 @@ -# Videos - -Generate and manage videos using video generation models. Create videos from text prompts, retrieve video status, and manage video files. - -## Capabilities - -### Create Video - -Generate a video from a text prompt. - -```python { .api } -def create( - self, - *, - prompt: str, - input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, - seconds: VideoSeconds | Omit = omit, - size: VideoSize | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: - """ - Create a video generation request. - - Args: - prompt: Text description of the desired video. - input_reference: Optional image reference that guides generation (FileTypes). - model: Video model to use (e.g., "sora-2"). Defaults to "sora-2". - seconds: Clip duration in seconds. Defaults to 4 seconds. - size: Output resolution formatted as width x height (e.g., "720x1280"). - Defaults to "720x1280". - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Video: Created video object with initial status. - """ -``` - -### Retrieve Video - -Get video status and details. - -```python { .api } -def retrieve( - self, - video_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: - """Retrieve video details and status.""" -``` - -### Poll for Completion - -Poll a video until it finishes processing (either completed or failed status). - -```python { .api } -def poll( - self, - video_id: str, - *, - poll_interval_ms: int | Omit = omit, -) -> Video: - """ - Wait for a video to finish processing. - - This method blocks until the video reaches "completed" or "failed" status. - It automatically retries with appropriate polling intervals. - - Args: - video_id: The ID of the video to poll. - poll_interval_ms: Custom polling interval in milliseconds. If not provided, - uses server-suggested interval or defaults to 1000ms. - - Returns: - Video: The video object with final status ("completed" or "failed"). - - Note: - This method returns even if the video failed to process. Check - video.status and video.error to handle failed cases. - """ -``` - -### List Videos - -List all videos with pagination. - -```python { .api } -def list( - self, - *, - after: str | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> SyncConversationCursorPage[Video]: - """ - List videos with pagination. - - Args: - after: Identifier for the last item from the previous pagination request. - limit: Number of items to retrieve. - order: Sort order by timestamp ("asc" or "desc"). - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - SyncConversationCursorPage[Video]: Paginated list of videos. - """ -``` - -### Delete Video - -Delete a video. - -```python { .api } -def delete( - self, - video_id: str, - *, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> VideoDeleteResponse: - """Delete a video.""" -``` - -### Download Video Content - -Download the generated video file. - -```python { .api } -def download_content( - self, - video_id: str, - *, - variant: Literal["video", "thumbnail", "spritesheet"] | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> HttpxBinaryResponseContent: - """ - Download video content as binary data. - - Args: - video_id: The ID of the video to download. - variant: The type of content to download. Options: - - "video": The full video file (default) - - "thumbnail": A thumbnail image of the video - - "spritesheet": A spritesheet of video frames - - Returns: - HttpxBinaryResponseContent: Binary video file content. - """ -``` - -### Remix Video - -Create a new video by remixing an existing video with a new prompt. - -```python { .api } -def remix( - self, - video_id: str, - *, - prompt: str, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: - """ - Create a remix of an existing video with a new prompt. - - Remixing uses the original video's model, duration, and size settings. - Only the prompt can be changed to create variations. - - Args: - video_id: The ID of the video to remix. - prompt: New creative prompt to guide the remix. - - Returns: - Video: Remix video object (status "queued" initially). - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create initial video -video = client.videos.create_and_poll( - prompt="A serene sunset over mountains" -) - -# Download the generated video -content = client.videos.download_content(video.id) -with open("sunset.mp4", "wb") as f: - f.write(content.content) - -# Create a remix with different prompt -remix = client.videos.remix( - video.id, - prompt="Transform the sunset to a dramatic thunderstorm" -) - -# Poll for remix completion -while remix.status != "completed": - time.sleep(5) - remix = client.videos.retrieve(remix.id) - -# Download remix -remix_content = client.videos.download_content(remix.id) -with open("storm.mp4", "wb") as f: - f.write(remix_content.content) -``` - -### Create and Poll - -Create video and wait for completion. - -```python { .api } -def create_and_poll( - self, - *, - prompt: str, - input_reference: FileTypes | Omit = omit, - model: VideoModel | Omit = omit, - seconds: VideoSeconds | Omit = omit, - size: VideoSize | Omit = omit, - poll_interval_ms: int | Omit = omit, - extra_headers: dict[str, str] | None = None, - extra_query: dict[str, object] | None = None, - extra_body: dict[str, object] | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, -) -> Video: - """ - Create video and poll until generation completes. - - Args: - prompt: Text description of the desired video. - input_reference: Optional image reference that guides generation. - model: Video model to use. Defaults to "sora-2". - seconds: Clip duration in seconds. - size: Output resolution. - poll_interval_ms: Polling interval in milliseconds. - extra_headers: Additional HTTP headers. - extra_query: Additional query parameters. - extra_body: Additional JSON fields. - timeout: Request timeout in seconds. - - Returns: - Video: Completed video object. - """ -``` - -Usage examples: - -```python -from openai import OpenAI - -client = OpenAI() - -# Create video -video = client.videos.create( - prompt="A serene mountain landscape at sunset", - model="sora-2", - seconds=5, - size="720x1280" -) - -print(f"Video ID: {video.id}") -print(f"Status: {video.status}") - -# Create and wait for completion -video = client.videos.create_and_poll( - prompt="Waves crashing on a beach", - model="sora-2" -) - -print(f"Final status: {video.status}") -if video.status == "completed" and video.url: - print(f"Video URL: {video.url}") - -# Check status -video = client.videos.retrieve("video_abc123") - -# List videos -videos = client.videos.list(limit=10) - -for video in videos.data: - print(f"{video.id}: {video.status}") - -# Delete video -result = client.videos.delete("video_abc123") -print(f"Deleted: {result.deleted}") -``` - -## Types - -```python { .api } -from typing import Literal -from pydantic import BaseModel - -class Video(BaseModel): - """Video object.""" - id: str - created_at: int - model: str - prompt: str - status: Literal["queued", "processing", "completed", "failed"] - url: str | None # Available when completed - error: dict | None # Present if failed - duration: int | None - size: str | None - -class VideoDeleteResponse(BaseModel): - """Deletion confirmation.""" - id: str - deleted: bool - -VideoModel = Literal["sora-2", "sora-2-pro"] -``` - -## Async Usage - -```python -import asyncio -from openai import AsyncOpenAI - -async def create_video(): - client = AsyncOpenAI() - - video = await client.videos.create( - model="video-gen-1", - prompt="A futuristic city" - ) - - return video.id - -video_id = asyncio.run(create_video()) -``` diff --git a/.tessl/tiles/tessl/pypi-openai/docs/webhooks.md b/.tessl/tiles/tessl/pypi-openai/docs/webhooks.md deleted file mode 100644 index c3e1e504d..000000000 --- a/.tessl/tiles/tessl/pypi-openai/docs/webhooks.md +++ /dev/null @@ -1,503 +0,0 @@ -# Webhooks - -Verify and handle webhook events from OpenAI for asynchronous notifications about fine-tuning jobs, batch completions, and other events. - -## Capabilities - -### Verify Webhook Signature - -Verify that a webhook request came from OpenAI. - -```python { .api } -def verify_signature( - payload: str | bytes, - headers: dict[str, str] | list[tuple[str, str]], - *, - secret: str | None = None, - tolerance: int = 300 -) -> None: - """ - Verify webhook signature to ensure authenticity. - - Args: - payload: Raw request body (bytes or string). - headers: Request headers containing webhook-signature, webhook-timestamp, - and webhook-id. Can be dict or list of tuples. - secret: Webhook secret from OpenAI dashboard. If None, uses client's - webhook_secret or OPENAI_WEBHOOK_SECRET environment variable. - tolerance: Maximum age of webhook in seconds (default: 300). - Used to prevent replay attacks. - - Returns: - None: If signature is valid. - - Raises: - InvalidWebhookSignatureError: If signature is invalid or timestamp is too old/new. - ValueError: If secret is not provided and not set on client. - """ -``` - -Usage example: - -```python -from openai import OpenAI, InvalidWebhookSignatureError - -client = OpenAI(webhook_secret="your-webhook-secret") - -# In your webhook endpoint -def webhook_handler(request): - payload = request.body # Raw bytes - headers = request.headers # Headers dict or list - - try: - # Verify signature - client.webhooks.verify_signature( - payload=payload, - headers=headers, - secret="your-webhook-secret" # Optional if set on client - ) - - # Signature valid, process webhook - event = json.loads(payload) - handle_webhook_event(event) - - except InvalidWebhookSignatureError: - # Invalid signature, reject request - return {"error": "Invalid signature"}, 401 -``` - -### Unwrap Webhook Event - -Verify signature and parse webhook event in one call. - -```python { .api } -def unwrap( - payload: str | bytes, - headers: dict[str, str] | list[tuple[str, str]], - *, - secret: str | None = None -) -> UnwrapWebhookEvent: - """ - Verify signature and parse webhook event. - - Args: - payload: Raw request body (bytes or string). - headers: Request headers containing webhook-signature, webhook-timestamp, - and webhook-id. Can be dict or list of tuples. - secret: Webhook secret from OpenAI dashboard. If None, uses client's - webhook_secret or OPENAI_WEBHOOK_SECRET environment variable. - - Returns: - UnwrapWebhookEvent: Parsed and verified webhook event. - - Raises: - InvalidWebhookSignatureError: If signature is invalid. - ValueError: If secret is not provided and not set on client. - """ -``` - -Usage example: - -```python -from openai import OpenAI - -client = OpenAI(webhook_secret="your-webhook-secret") - -# In webhook endpoint -def webhook_handler(request): - try: - # Verify and parse in one call - event = client.webhooks.unwrap( - payload=request.body, - headers=request.headers, - secret="your-webhook-secret" # Optional if set on client - ) - - # Handle different event types - if event.type == "fine_tuning.job.succeeded": - handle_fine_tuning_success(event.data) - - elif event.type == "batch.completed": - handle_batch_completion(event.data) - - return {"status": "ok"}, 200 - - except Exception as e: - return {"error": str(e)}, 400 -``` - -## Webhook Event Types - -### Fine-tuning Events - -```python -# Job succeeded -{ - "type": "fine_tuning.job.succeeded", - "data": { - "id": "ftjob-abc123", - "fine_tuned_model": "ft:gpt-3.5-turbo:org:model:abc", - "status": "succeeded" - } -} - -# Job failed -{ - "type": "fine_tuning.job.failed", - "data": { - "id": "ftjob-abc123", - "status": "failed", - "error": {...} - } -} - -# Job cancelled -{ - "type": "fine_tuning.job.cancelled", - "data": { - "id": "ftjob-abc123", - "status": "cancelled" - } -} -``` - -### Batch Events - -```python -# Batch completed -{ - "type": "batch.completed", - "data": { - "id": "batch_abc123", - "status": "completed", - "output_file_id": "file-xyz789" - } -} - -# Batch failed -{ - "type": "batch.failed", - "data": { - "id": "batch_abc123", - "status": "failed", - "errors": {...} - } -} - -# Batch cancelled -{ - "type": "batch.cancelled", - "data": { - "id": "batch_abc123", - "status": "cancelled" - } -} - -# Batch expired -{ - "type": "batch.expired", - "data": { - "id": "batch_abc123", - "status": "expired" - } -} -``` - -### Eval Events - -```python -# Eval run succeeded -{ - "type": "eval.run.succeeded", - "data": { - "id": "eval_run_abc123", - "status": "succeeded", - "results": {...} - } -} - -# Eval run failed -{ - "type": "eval.run.failed", - "data": { - "id": "eval_run_abc123", - "status": "failed", - "error": {...} - } -} -``` - -## Types - -```python { .api } -from typing import Union, Literal -from typing_extensions import TypeAlias - -# UnwrapWebhookEvent is a union of all possible webhook event types -UnwrapWebhookEvent: TypeAlias = Union[ - BatchCancelledWebhookEvent, - BatchCompletedWebhookEvent, - BatchExpiredWebhookEvent, - BatchFailedWebhookEvent, - EvalRunCanceledWebhookEvent, - EvalRunFailedWebhookEvent, - EvalRunSucceededWebhookEvent, - FineTuningJobCancelledWebhookEvent, - FineTuningJobFailedWebhookEvent, - FineTuningJobSucceededWebhookEvent, - RealtimeCallIncomingWebhookEvent, - ResponseCancelledWebhookEvent, - ResponseCompletedWebhookEvent, - ResponseFailedWebhookEvent, - ResponseIncompleteWebhookEvent, -] - -# Fine-tuning event types -class FineTuningJobSucceededWebhookEvent: - type: Literal["fine_tuning.job.succeeded"] - data: dict - -class FineTuningJobFailedWebhookEvent: - type: Literal["fine_tuning.job.failed"] - data: dict - -class FineTuningJobCancelledWebhookEvent: - type: Literal["fine_tuning.job.cancelled"] - data: dict - -# Batch event types -class BatchCompletedWebhookEvent: - type: Literal["batch.completed"] - data: dict - -class BatchFailedWebhookEvent: - type: Literal["batch.failed"] - data: dict - -class BatchCancelledWebhookEvent: - type: Literal["batch.cancelled"] - data: dict - -class BatchExpiredWebhookEvent: - type: Literal["batch.expired"] - data: dict - -# Eval event types -class EvalRunSucceededWebhookEvent: - type: Literal["eval.run.succeeded"] - data: dict - -class EvalRunFailedWebhookEvent: - type: Literal["eval.run.failed"] - data: dict - -class EvalRunCanceledWebhookEvent: - type: Literal["eval.run.canceled"] - data: dict - -# Response event types -class ResponseCompletedWebhookEvent: - type: Literal["response.completed"] - data: dict - -class ResponseFailedWebhookEvent: - type: Literal["response.failed"] - data: dict - -class ResponseCancelledWebhookEvent: - type: Literal["response.cancelled"] - data: dict - -class ResponseIncompleteWebhookEvent: - type: Literal["response.incomplete"] - data: dict - -# Realtime event types -class RealtimeCallIncomingWebhookEvent: - type: Literal["realtime.call.incoming"] - data: dict -``` - -## Complete Webhook Handler - -```python -from openai import OpenAI, InvalidWebhookSignatureError -from flask import Flask, request, jsonify - -app = Flask(__name__) -client = OpenAI() - -WEBHOOK_SECRET = "your-webhook-secret" - -@app.route("/webhooks/openai", methods=["POST"]) -def handle_webhook(): - # Get headers and payload - payload = request.data - headers = request.headers - - # Verify and parse - try: - event = client.webhooks.unwrap( - payload=payload, - headers=headers, - secret=WEBHOOK_SECRET - ) - except InvalidWebhookSignatureError: - return jsonify({"error": "Invalid signature"}), 401 - - # Handle event - if event.type == "fine_tuning.job.succeeded": - job_id = event.data["id"] - model = event.data["fine_tuned_model"] - print(f"Fine-tuning succeeded: {job_id} -> {model}") - - # Deploy model or notify user - deploy_model(model) - - elif event.type == "batch.completed": - batch_id = event.data["id"] - output_file = event.data["output_file_id"] - print(f"Batch completed: {batch_id}") - - # Process results - process_batch_results(batch_id, output_file) - - elif event.type == "fine_tuning.job.failed": - job_id = event.data["id"] - error = event.data.get("error") - print(f"Fine-tuning failed: {job_id}, Error: {error}") - - # Notify user of failure - notify_failure(job_id, error) - - return jsonify({"status": "received"}), 200 - -if __name__ == "__main__": - app.run(port=8080) -``` - -## Best Practices - -```python -from openai import OpenAI, InvalidWebhookSignatureError -import hmac -import hashlib - -client = OpenAI() - -# 1. Always verify signatures -def is_valid_webhook(payload: bytes, headers: dict, secret: str) -> bool: - try: - client.webhooks.verify_signature(payload, headers, secret=secret) - return True - except InvalidWebhookSignatureError: - return False - -# 2. Handle replay attacks with timestamp -def is_recent_webhook(timestamp: str, max_age_seconds: int = 300) -> bool: - import time - - event_time = int(timestamp) - current_time = int(time.time()) - - return (current_time - event_time) < max_age_seconds - -# 3. Process events idempotently -processed_events = set() - -def process_webhook_event(event_id: str, event_data: dict): - if event_id in processed_events: - print(f"Duplicate event: {event_id}") - return - - # Process event - handle_event(event_data) - - # Mark as processed - processed_events.add(event_id) - -# 4. Return 200 quickly, process async -from threading import Thread - -def handle_webhook_async(event): - # Process in background - thread = Thread(target=process_event, args=(event,)) - thread.start() - - # Return immediately - return {"status": "accepted"}, 200 - -# 5. Retry on failure -import time - -def process_with_retry(event, max_retries=3): - for attempt in range(max_retries): - try: - process_event(event) - return - except Exception as e: - if attempt == max_retries - 1: - log_failure(event, e) - raise - time.sleep(2 ** attempt) -``` - -## Testing Webhooks - -```python -# Generate test signature for development -import hmac -import hashlib -import json -import time - -def generate_test_signature(payload: dict, secret: str) -> tuple[str, str]: - """Generate signature for testing.""" - timestamp = str(int(time.time())) - payload_str = json.dumps(payload) - - # Create signature - message = f"{timestamp}.{payload_str}" - signature = hmac.new( - secret.encode(), - message.encode(), - hashlib.sha256 - ).hexdigest() - - return signature, timestamp - -# Test webhook handler -test_payload = { - "type": "fine_tuning.job.succeeded", - "data": { - "id": "ftjob-test123", - "fine_tuned_model": "ft:gpt-3.5-turbo:test" - } -} - -# Create test headers (actual implementation would vary) -test_headers = { - "webhook-signature": "v1,test_signature", - "webhook-timestamp": str(int(time.time())), - "webhook-id": "test_webhook_id" -} - -# Note: In production, signatures are generated by OpenAI -# This is a simplified example for testing -event = client.webhooks.unwrap( - payload=json.dumps(test_payload), - headers=test_headers, - secret=WEBHOOK_SECRET -) - -print(f"Test event: {event.type}") -``` - -## Security Considerations - -1. **Always verify signatures** - Never trust webhook data without verification -2. **Use HTTPS** - Only accept webhooks over HTTPS -3. **Check timestamps** - Reject old events to prevent replay attacks -4. **Rate limit** - Limit webhook endpoint to prevent abuse -5. **Store secrets securely** - Use environment variables or secret management -6. **Log failures** - Track verification failures for security monitoring diff --git a/.tessl/tiles/tessl/pypi-openai/tile.json b/.tessl/tiles/tessl/pypi-openai/tile.json deleted file mode 100644 index e16c0fa0c..000000000 --- a/.tessl/tiles/tessl/pypi-openai/tile.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "tessl/pypi-openai", - "version": "2.11.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/openai@2.11.0", - "summary": "The official Python library for the openai API", - "private": false -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md deleted file mode 100644 index ff4beb577..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/administration.md +++ /dev/null @@ -1,27 +0,0 @@ -# Administration & User Management - -Administrative functions for managing users, workspaces, invitations, and organizational settings. - -## Capabilities - -```python { .api } -class Admin: - users: Users - invites: Invites - workspaces: Workspaces - -class Users: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - -class Workspaces: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - users: WorkspacesUsers -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md deleted file mode 100644 index 931774627..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/assistants-threads.md +++ /dev/null @@ -1,71 +0,0 @@ -# Assistants & Threads - -OpenAI Assistants API implementation with thread management, message handling, and tool execution support. - -## Capabilities - -### Assistant Management - -```python { .api } -class Assistants: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - -class Threads: - def create(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - messages: Messages - runs: Runs - -class Messages: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - -class Runs: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def cancel(self, **kwargs): ... - steps: Steps -``` - -## Usage Examples - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Create assistant -assistant = portkey.beta.assistants.create( - name="Math Tutor", - instructions="You are a helpful math tutor", - model="gpt-4" -) - -# Create thread -thread = portkey.beta.threads.create() - -# Add message -portkey.beta.threads.messages.create( - thread_id=thread.id, - role="user", - content="Solve 2x + 3 = 7" -) - -# Create run -run = portkey.beta.threads.runs.create( - thread_id=thread.id, - assistant_id=assistant.id -) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md deleted file mode 100644 index 1c3b1ab19..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/batch-processing.md +++ /dev/null @@ -1,27 +0,0 @@ -# Batch Processing - -Batch API for processing large volumes of requests efficiently with cost optimization and result management. - -## Capabilities - -```python { .api } -class Batches: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def cancel(self, **kwargs): ... -``` - -## Usage Examples - -```python -# Create batch job -batch = portkey.batches.create( - input_file_id="file-123", - endpoint="/v1/chat/completions", - completion_window="24h" -) - -# Check batch status -batch_status = portkey.batches.retrieve(batch.id) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md deleted file mode 100644 index 95369bef1..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/beta-realtime.md +++ /dev/null @@ -1,290 +0,0 @@ -# Beta Realtime API - -Real-time audio and WebSocket-based AI interactions for building conversational applications with low-latency voice communication. Supports real-time session management and WebSocket connections for streaming audio communication. - -## Capabilities - -### Real-Time Connection Management - -Establishes WebSocket connections for real-time communication with AI models, enabling low-latency voice and audio interactions. - -```python { .api } -class BetaRealtime: - def connect( - self, - *, - model: str, - websocket_connection_options: WebsocketConnectionOptions = {}, - **kwargs - ) -> RealtimeConnectionManager: - """ - Create a real-time WebSocket connection to an AI model. - - Args: - model: Model identifier for real-time communication - websocket_connection_options: WebSocket configuration options - **kwargs: Additional connection parameters - - Returns: - RealtimeConnectionManager: Connection manager for real-time communication - """ - - sessions: BetaSessions - -class AsyncBetaRealtime: - def connect( - self, - *, - model: str, - websocket_connection_options: WebsocketConnectionOptions = {}, - **kwargs - ) -> AsyncRealtimeConnectionManager: - """Async version of connect method.""" - - sessions: AsyncBetaSessions -``` - -### Real-Time Session Management - -Create and manage real-time sessions with configurable audio formats, voice settings, and interaction parameters. - -```python { .api } -class BetaSessions: - def create( - self, - *, - model: Any = "portkey-default", - input_audio_format: Union[Any, NotGiven] = NOT_GIVEN, - input_audio_transcription: Union[Any, NotGiven] = NOT_GIVEN, - instructions: Union[str, NotGiven] = NOT_GIVEN, - max_response_output_tokens: Union[int, Any, NotGiven] = NOT_GIVEN, - modalities: Union[List[Any], NotGiven] = NOT_GIVEN, - output_audio_format: Union[Any, NotGiven] = NOT_GIVEN, - temperature: Union[float, NotGiven] = NOT_GIVEN, - tool_choice: Union[str, NotGiven] = NOT_GIVEN, - tools: Union[Iterable[Any], NotGiven] = NOT_GIVEN, - turn_detection: Union[Any, NotGiven] = NOT_GIVEN, - voice: Union[Any, NotGiven] = NOT_GIVEN - ) -> SessionCreateResponse: - """ - Create a real-time session for voice communication. - - Args: - model: Model to use for the session - input_audio_format: Format for input audio (e.g., "pcm16", "g711_ulaw") - input_audio_transcription: Configuration for input audio transcription - instructions: System instructions for the AI assistant - max_response_output_tokens: Maximum tokens in response - modalities: Supported modalities (audio, text) - output_audio_format: Format for output audio - temperature: Response randomness (0.0 to 2.0) - tool_choice: Tool selection strategy - tools: Available tools for the assistant - turn_detection: Turn detection configuration - voice: Voice model for audio output - - Returns: - SessionCreateResponse: Session configuration and connection details - """ - -class AsyncBetaSessions: - async def create( - self, - *, - model: Any = "portkey-default", - input_audio_format: Union[Any, NotGiven] = NOT_GIVEN, - input_audio_transcription: Union[Any, NotGiven] = NOT_GIVEN, - instructions: Union[str, NotGiven] = NOT_GIVEN, - max_response_output_tokens: Union[int, Any, NotGiven] = NOT_GIVEN, - modalities: Union[List[Any], NotGiven] = NOT_GIVEN, - output_audio_format: Union[Any, NotGiven] = NOT_GIVEN, - temperature: Union[float, NotGiven] = NOT_GIVEN, - tool_choice: Union[str, NotGiven] = NOT_GIVEN, - tools: Union[Iterable[Any], NotGiven] = NOT_GIVEN, - turn_detection: Union[Any, NotGiven] = NOT_GIVEN, - voice: Union[Any, NotGiven] = NOT_GIVEN - ) -> SessionCreateResponse: - """Async version of session creation.""" -``` - -### Usage Examples - -```python -from portkey_ai import Portkey - -# Initialize client -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Create a real-time session -session = portkey.beta.realtime.sessions.create( - model="gpt-4-realtime-preview", - modalities=["text", "audio"], - instructions="You are a helpful voice assistant.", - voice="alloy", - input_audio_format="pcm16", - output_audio_format="pcm16", - turn_detection={ - "type": "server_vad", - "threshold": 0.5, - "prefix_padding_ms": 300, - "silence_duration_ms": 200 - } -) - -print(f"Session ID: {session.id}") -print(f"Model: {session.model}") - -# Establish WebSocket connection -connection = portkey.beta.realtime.connect( - model="gpt-4-realtime-preview", - websocket_connection_options={ - "timeout": 30, - "additional_headers": { - "Authorization": f"Bearer {portkey.api_key}" - } - } -) - -# Use connection for real-time communication -# Note: Actual usage would involve WebSocket event handling -with connection as conn: - # Send audio data - conn.send_audio_data(audio_bytes) - - # Handle responses - for event in conn.listen(): - if event.type == "response.audio.delta": - # Process audio response - process_audio_chunk(event.delta) - elif event.type == "response.text.delta": - # Process text response - print(event.delta, end="") -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def create_realtime_session(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - # Create session asynchronously - session = await portkey.beta.realtime.sessions.create( - model="gpt-4-realtime-preview", - modalities=["text", "audio"], - instructions="You are a voice assistant for customer support.", - voice="nova", - temperature=0.7, - max_response_output_tokens=150 - ) - - # Establish async connection - connection = portkey.beta.realtime.connect( - model="gpt-4-realtime-preview" - ) - - return session, connection - -# Run async function -session, connection = asyncio.run(create_realtime_session()) -``` - -### Advanced Configuration - -```python -# Configure detailed session parameters -session = portkey.beta.realtime.sessions.create( - model="gpt-4-realtime-preview", - modalities=["text", "audio"], - instructions=""" - You are an AI assistant for a language learning app. - Help users practice pronunciation and provide feedback. - Speak clearly and at a moderate pace. - """, - voice="shimmer", - input_audio_format="pcm16", - output_audio_format="pcm16", - input_audio_transcription={ - "model": "whisper-1" - }, - turn_detection={ - "type": "server_vad", - "threshold": 0.6, - "prefix_padding_ms": 300, - "silence_duration_ms": 500 - }, - tools=[ - { - "type": "function", - "name": "pronunciation_feedback", - "description": "Provide pronunciation feedback", - "parameters": { - "type": "object", - "properties": { - "word": {"type": "string"}, - "accuracy": {"type": "number"}, - "feedback": {"type": "string"} - } - } - } - ], - tool_choice="auto", - temperature=0.3, - max_response_output_tokens=100 -) -``` - -## Types - -```python { .api } -class SessionCreateResponse: - """Response from real-time session creation""" - id: str # Session identifier - object: str # "realtime.session" - model: str # Model used for the session - modalities: List[str] # Supported modalities - instructions: str # System instructions - voice: str # Voice model - input_audio_format: str # Input audio format - output_audio_format: str # Output audio format - input_audio_transcription: dict # Transcription settings - turn_detection: dict # Turn detection configuration - tools: List[dict] # Available tools - tool_choice: str # Tool selection strategy - temperature: float # Response temperature - max_response_output_tokens: int # Token limit - _headers: Optional[dict] # Response headers - -class RealtimeConnectionManager: - """Synchronous WebSocket connection manager""" - def send_audio_data(self, audio_bytes: bytes) -> None: ... - def listen(self) -> Iterator[RealtimeEvent]: ... - def close(self) -> None: ... - -class AsyncRealtimeConnectionManager: - """Asynchronous WebSocket connection manager""" - async def send_audio_data(self, audio_bytes: bytes) -> None: ... - async def listen(self) -> AsyncIterator[RealtimeEvent]: ... - async def close(self) -> None: ... - -class WebsocketConnectionOptions: - """WebSocket connection configuration""" - timeout: Optional[int] # Connection timeout in seconds - additional_headers: Optional[dict] # Additional headers - # Additional WebSocket-specific options - -class RealtimeEvent: - """Real-time event from WebSocket connection""" - type: str # Event type - delta: Optional[str] # Content delta for streaming - audio: Optional[bytes] # Audio data - # Additional event-specific fields -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md deleted file mode 100644 index c508c25e5..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/chat-completions.md +++ /dev/null @@ -1,364 +0,0 @@ -# Chat Completions - -OpenAI-compatible chat completion API with support for all major providers, streaming, function calling, and advanced Portkey features like fallbacks and load balancing. - -## Capabilities - -### Chat Completion Creation - -Primary method for generating chat completions with support for multi-turn conversations, function calling, and streaming responses. - -```python { .api } -class ChatCompletion: - """Synchronous chat completion API""" - completions: Completions - -class Completions: - """Chat completions endpoint""" - - def create( - self, - *, - messages: Iterable[dict], - model: Optional[str] = "portkey-default", - stream: Optional[bool] = None, - temperature: Optional[float] = None, - max_tokens: Optional[int] = None, - top_p: Optional[float] = None, - audio: Optional[Any] = None, - max_completion_tokens: Optional[int] = None, - metadata: Optional[Dict[str, str]] = None, - modalities: Optional[List[Any]] = None, - prediction: Optional[Any] = None, - reasoning_effort: Optional[Any] = None, - store: Optional[bool] = None, - **kwargs - ) -> Union[ChatCompletions, Iterator[ChatCompletionChunk]]: - """ - Create a chat completion. - - Parameters: - - messages: List of message objects with 'role' and 'content' - - model: Model identifier (defaults to 'portkey-default') - - max_tokens: Maximum tokens to generate - - temperature: Sampling temperature - - stream: Enable streaming responses - - top_p: Nucleus sampling parameter - - audio: Audio configuration for multimodal models - - max_completion_tokens: Maximum completion tokens - - metadata: Custom metadata for request - - modalities: Supported modalities (text, audio, etc.) - - prediction: Prediction configuration - - reasoning_effort: Reasoning effort level - - store: Whether to store the conversation - - Returns: - ChatCompletions object or Iterator of ChatCompletionChunk for streaming - """ - - messages: ChatCompletionsMessages - -class AsyncChatCompletion: - """Asynchronous chat completion API""" - completions: AsyncCompletions - -class AsyncCompletions: - """Async chat completions endpoint""" - - async def create( - self, - *, - messages: Iterable[dict], - model: Optional[str] = "portkey-default", - **kwargs - ) -> Union[ChatCompletions, AsyncIterator[ChatCompletionChunk]]: - """Async version of chat completion creation""" -``` - -### Message Management - -Handle multi-turn conversations with proper message formatting and conversation context management. - -```python { .api } -class ChatCompletionsMessages: - """Message handling utilities for chat completions""" - - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - -class AsyncChatCompletionsMessages: - """Async message handling utilities""" - - async def create(self, **kwargs): ... - async def list(self, **kwargs): ... - async def retrieve(self, **kwargs): ... - async def update(self, **kwargs): ... - async def delete(self, **kwargs): ... -``` - -## Usage Examples - -### Basic Chat Completion - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Simple chat completion -response = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "What is machine learning?"} - ], - model="gpt-4" -) - -print(response.choices[0].message.content) -``` - -### Multi-turn Conversation - -```python -# Multi-turn conversation -messages = [ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Explain quantum computing"}, - {"role": "assistant", "content": "Quantum computing is..."}, - {"role": "user", "content": "What are its main applications?"} -] - -response = portkey.chat.completions.create( - messages=messages, - model="gpt-4", - max_tokens=500, - temperature=0.7 -) - -print(response.choices[0].message.content) -``` - -### Streaming Response - -```python -# Streaming chat completion -stream = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "Write a story about AI"} - ], - model="gpt-4", - stream=True -) - -for chunk in stream: - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") -``` - -### Function Calling - -```python -# Function calling with tools -tools = [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get weather information for a location", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "City name" - }, - "unit": { - "type": "string", - "enum": ["celsius", "fahrenheit"], - "description": "Temperature unit" - } - }, - "required": ["location"] - } - } - } -] - -response = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "What's the weather in San Francisco?"} - ], - model="gpt-4", - tools=tools, - tool_choice="auto" -) - -# Handle function call -if response.choices[0].message.tool_calls: - tool_call = response.choices[0].message.tool_calls[0] - function_name = tool_call.function.name - function_args = tool_call.function.arguments - print(f"Function called: {function_name} with args: {function_args}") -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def chat_example(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - response = await portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "Hello, how are you?"} - ], - model="gpt-4" - ) - - print(response.choices[0].message.content) - - # Async streaming - async for chunk in await portkey.chat.completions.create( - messages=[{"role": "user", "content": "Count to 10"}], - model="gpt-4", - stream=True - ): - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") - -asyncio.run(chat_example()) -``` - -### Response Format Control - -```python -# JSON response format -response = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "List 3 colors in JSON format"} - ], - model="gpt-4", - response_format={"type": "json_object"} -) - -# Structured output with seed for reproducibility -response = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "Generate a random number"} - ], - model="gpt-4", - seed=12345, - temperature=0 -) -``` - -## Message Format - -### Message Structure - -```python -# Message object structure -message = { - "role": "user" | "assistant" | "system" | "tool", - "content": str | List[dict], # Text or multimodal content - "name": str, # Optional message name - "tool_calls": List[dict], # Tool calls (assistant messages) - "tool_call_id": str # Tool call ID (tool messages) -} -``` - -### Multimodal Content - -```python -# Text and image message -message = { - "role": "user", - "content": [ - { - "type": "text", - "text": "What's in this image?" - }, - { - "type": "image_url", - "image_url": { - "url": "https://example.com/image.jpg", - "detail": "high" - } - } - ] -} -``` - -## Advanced Features - -### Provider-Specific Parameters - -Different providers support various additional parameters: - -```python -# OpenAI-specific parameters -response = portkey.chat.completions.create( - messages=messages, - model="gpt-4", - logprobs=True, - top_logprobs=3, - logit_bias={50256: -100} # Reduce likelihood of specific tokens -) - -# Anthropic-specific parameters -response = portkey.chat.completions.create( - messages=messages, - model="claude-3-sonnet-20240229", - max_tokens=1000, - anthropic_version="2023-06-01" -) -``` - -### Portkey-Specific Features - -```python -# Multiple providers with fallback -portkey = Portkey( - api_key="PORTKEY_API_KEY", - config={ - "strategy": {"mode": "fallback"}, - "targets": [ - {"provider": "openai", "api_key": "OPENAI_KEY"}, - {"provider": "anthropic", "api_key": "ANTHROPIC_KEY"} - ] - } -) - -# Load balancing across providers -portkey = Portkey( - api_key="PORTKEY_API_KEY", - config={ - "strategy": {"mode": "loadbalance"}, - "targets": [ - {"provider": "openai", "api_key": "OPENAI_KEY", "weight": 0.7}, - {"provider": "anthropic", "api_key": "ANTHROPIC_KEY", "weight": 0.3} - ] - } -) - -# Request with custom metadata -response = portkey.chat.completions.create( - messages=messages, - model="gpt-4", - metadata={ - "user_id": "user123", - "session_id": "session456", - "environment": "production" - } -) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md deleted file mode 100644 index c193eb815..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/configuration-management.md +++ /dev/null @@ -1,27 +0,0 @@ -# Configuration Management - -Advanced configuration system for managing provider settings, routing rules, fallback strategies, and load balancing configurations. - -## Capabilities - -```python { .api } -class Configs: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` - -## Usage Examples - -```python -# Create configuration -config = portkey.configs.create({ - "strategy": {"mode": "fallback"}, - "targets": [ - {"provider": "openai", "api_key": "key1"}, - {"provider": "anthropic", "api_key": "key2"} - ] -}) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md deleted file mode 100644 index 8afcc90f6..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/container-content.md +++ /dev/null @@ -1,20 +0,0 @@ -# Container & Content Management - -Container-based deployment and content management capabilities for scalable AI applications. - -## Capabilities - -```python { .api } -class Containers: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - files: ContainersFiles - -class Content: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md deleted file mode 100644 index 40a6745ba..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/core-client.md +++ /dev/null @@ -1,274 +0,0 @@ -# Core Client & Configuration - -Primary client classes and configuration utilities for initializing and managing Portkey connections with comprehensive provider support and advanced routing capabilities. - -## Capabilities - -### Main Client Classes - -Core synchronous and asynchronous client classes that provide the primary interface to Portkey's AI gateway and observability features. - -```python { .api } -class Portkey: - """ - Primary synchronous client for Portkey AI API. - - Provides unified interface to 40+ AI providers with built-in fallbacks, - load balancing, caching, and comprehensive observability. - """ - def __init__( - self, - *, - api_key: Optional[str] = None, - base_url: Optional[str] = None, - virtual_key: Optional[str] = None, - websocket_base_url: Optional[Union[str, httpx.URL]] = None, - config: Optional[Union[Mapping, str]] = None, - provider: Optional[str] = None, - trace_id: Optional[str] = None, - metadata: Union[Optional[dict[str, str]], str] = None, - cache_namespace: Optional[str] = None, - debug: Optional[bool] = None, - cache_force_refresh: Optional[bool] = None, - custom_host: Optional[str] = None, - forward_headers: Optional[List[str]] = None, - instrumentation: Optional[bool] = None, - openai_project: Optional[str] = None, - openai_organization: Optional[str] = None, - aws_secret_access_key: Optional[str] = None, - aws_access_key_id: Optional[str] = None, - aws_session_token: Optional[str] = None, - aws_region: Optional[str] = None, - vertex_project_id: Optional[str] = None, - vertex_region: Optional[str] = None, - workers_ai_account_id: Optional[str] = None, - azure_resource_name: Optional[str] = None, - azure_deployment_id: Optional[str] = None, - azure_api_version: Optional[str] = None, - azure_endpoint_name: Optional[str] = None, - huggingface_base_url: Optional[str] = None, - http_client: Optional[httpx.Client] = None, - request_timeout: Optional[int] = None, - strict_open_ai_compliance: Optional[bool] = False, - anthropic_beta: Optional[str] = None, - anthropic_version: Optional[str] = None, - mistral_fim_completion: Optional[str] = None, - vertex_storage_bucket_name: Optional[str] = None, - provider_file_name: Optional[str] = None, - provider_model: Optional[str] = None, - aws_s3_bucket: Optional[str] = None, - aws_s3_object_key: Optional[str] = None, - aws_bedrock_model: Optional[str] = None, - fireworks_account_id: Optional[str] = None, - calculate_audio_duration: Optional[bool] = True, - **kwargs - ) -> None: ... - -class AsyncPortkey: - """ - Asynchronous client for Portkey AI API. - - Async version of Portkey client with identical API surface - supporting concurrent operations and async/await patterns. - """ - def __init__(self, **kwargs) -> None: ... -``` - -### Usage Examples - -```python -# Basic initialization -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Advanced configuration with provider-specific settings -portkey = Portkey( - api_key="PORTKEY_API_KEY", - config={ - "strategy": { - "mode": "fallback" - }, - "targets": [ - { - "provider": "openai", - "api_key": "OPENAI_API_KEY" - }, - { - "provider": "anthropic", - "api_key": "ANTHROPIC_API_KEY" - } - ] - }, - metadata={ - "environment": "production", - "user_id": "user123" - }, - trace_id="trace-abc-123", - debug=True -) - -# Async client usage -import asyncio -from portkey_ai import AsyncPortkey - -async def main(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - response = await portkey.chat.completions.create( - messages=[{"role": "user", "content": "Hello"}], - model="gpt-4" - ) - - print(response) - -asyncio.run(main()) -``` - -### Header Creation Utilities - -Utility functions for creating and managing Portkey-specific request headers. - -```python { .api } -def createHeaders( - api_key: Optional[str] = None, - provider: Optional[str] = None, - trace_id: Optional[str] = None, - metadata: Optional[dict] = None, - config: Optional[Union[str, dict]] = None, - cache_namespace: Optional[str] = None, - cache_force_refresh: Optional[bool] = None, - virtual_key: Optional[str] = None, - custom_host: Optional[str] = None, - forward_headers: Optional[List[str]] = None, - openai_project: Optional[str] = None, - openai_organization: Optional[str] = None, - aws_secret_access_key: Optional[str] = None, - aws_access_key_id: Optional[str] = None, - aws_session_token: Optional[str] = None, - aws_region: Optional[str] = None, - vertex_project_id: Optional[str] = None, - vertex_region: Optional[str] = None, - workers_ai_account_id: Optional[str] = None, - azure_resource_name: Optional[str] = None, - azure_deployment_id: Optional[str] = None, - azure_api_version: Optional[str] = None, - azure_endpoint_name: Optional[str] = None, - anthropic_beta: Optional[str] = None, - anthropic_version: Optional[str] = None, - **kwargs -) -> dict: - """ - Create Portkey-specific headers for API requests. - - Returns: - Dictionary of headers for HTTP requests - """ -``` - -### Global Configuration Variables - -Module-level configuration variables for default settings. - -```python { .api } -api_key: Optional[str] -"""Global API key from environment variable PORTKEY_API_KEY""" - -base_url: Optional[str] -"""Global base URL from environment variable PORTKEY_PROXY_ENV or default""" - -config: Optional[Union[Mapping, str]] -"""Global configuration object""" - -mode: Optional[Union[Modes, ModesLiteral]] -"""Global mode setting""" -``` - -### Constants - -```python { .api } -PORTKEY_BASE_URL: str -"""Default Portkey API base URL""" - -PORTKEY_API_KEY_ENV: str -"""Environment variable name for API key""" - -PORTKEY_PROXY_ENV: str -"""Environment variable name for proxy URL""" - -PORTKEY_GATEWAY_URL: str -"""Gateway URL constant""" -``` - -## Configuration Parameters - -### Core Parameters - -- **api_key**: Portkey API key for authentication -- **base_url**: Base URL for API requests (defaults to Portkey gateway) -- **virtual_key**: Virtual key for secure credential management -- **config**: Configuration object for routing, fallbacks, and provider settings -- **provider**: Specific AI provider to use -- **trace_id**: Request tracing identifier for observability -- **metadata**: Custom metadata for request categorization and analytics - -### Caching Parameters - -- **cache_namespace**: Namespace for cache isolation -- **cache_force_refresh**: Force cache refresh for requests - -### Observability Parameters - -- **debug**: Enable debug mode for detailed logging -- **instrumentation**: Enable request instrumentation -- **forward_headers**: List of headers to forward to providers - -### Provider-Specific Parameters - -#### OpenAI -- **openai_project**: OpenAI project ID -- **openai_organization**: OpenAI organization ID - -#### AWS/Bedrock -- **aws_secret_access_key**: AWS secret access key -- **aws_access_key_id**: AWS access key ID -- **aws_session_token**: AWS session token -- **aws_region**: AWS region -- **aws_s3_bucket**: S3 bucket for file operations -- **aws_s3_object_key**: S3 object key -- **aws_bedrock_model**: Bedrock model identifier - -#### Google Vertex AI -- **vertex_project_id**: Vertex AI project ID -- **vertex_region**: Vertex AI region -- **vertex_storage_bucket_name**: Storage bucket name - -#### Azure OpenAI -- **azure_resource_name**: Azure resource name -- **azure_deployment_id**: Azure deployment ID -- **azure_api_version**: Azure API version -- **azure_endpoint_name**: Azure endpoint name - -#### Anthropic -- **anthropic_beta**: Anthropic beta features -- **anthropic_version**: Anthropic API version - -#### Other Providers -- **workers_ai_account_id**: Cloudflare Workers AI account ID -- **huggingface_base_url**: HuggingFace API base URL -- **fireworks_account_id**: Fireworks AI account ID -- **mistral_fim_completion**: Mistral fill-in-middle completion setting - -### HTTP Configuration - -- **http_client**: Custom HTTP client instance -- **request_timeout**: Request timeout in seconds -- **websocket_base_url**: WebSocket base URL for real-time features -- **custom_host**: Custom host override -- **strict_open_ai_compliance**: Enforce strict OpenAI API compliance \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md deleted file mode 100644 index e2a22c12c..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/embeddings.md +++ /dev/null @@ -1,140 +0,0 @@ -# Embeddings - -Text embedding generation for converting text into numerical vectors for semantic similarity, search, and machine learning applications. Supports multiple providers through Portkey's unified interface. - -## Capabilities - -### Embedding Creation - -Creates vector embeddings from input text using the specified model. Supports various embedding models from different providers with consistent interface. - -```python { .api } -class Embeddings: - def create( - self, - *, - input: str, - model: Optional[str] = "portkey-default", - dimensions: Union[int, NotGiven] = NOT_GIVEN, - encoding_format: Union[str, NotGiven] = NOT_GIVEN, - user: Union[str, NotGiven] = NOT_GIVEN, - **kwargs - ) -> CreateEmbeddingResponse: - """ - Create embeddings for the given input text. - - Args: - input: Input text to create embeddings for - model: Embedding model to use (defaults to "portkey-default") - dimensions: Number of dimensions for the embedding vectors (model-dependent) - encoding_format: Format for encoding the embeddings (e.g., "float", "base64") - user: User identifier for tracking and analytics - **kwargs: Additional provider-specific parameters - - Returns: - CreateEmbeddingResponse: Response containing embedding vectors and metadata - """ - -class AsyncEmbeddings: - async def create( - self, - *, - input: str, - model: Optional[str] = "portkey-default", - dimensions: Union[int, NotGiven] = NOT_GIVEN, - encoding_format: Union[str, NotGiven] = NOT_GIVEN, - user: Union[str, NotGiven] = NOT_GIVEN, - **kwargs - ) -> CreateEmbeddingResponse: - """Async version of create method.""" -``` - -### Usage Examples - -```python -from portkey_ai import Portkey - -# Initialize client -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Create embeddings for single text -response = portkey.embeddings.create( - input="Hello, world!", - model="text-embedding-ada-002" -) - -# Access the embedding vector -embedding = response.data[0].embedding -print(f"Embedding dimensions: {len(embedding)}") - -# Create embeddings with specific dimensions -response = portkey.embeddings.create( - input="Text to embed", - model="text-embedding-3-small", - dimensions=512 -) - -# Batch embedding creation (provider-dependent) -texts = [ - "First text to embed", - "Second text to embed", - "Third text to embed" -] - -for text in texts: - response = portkey.embeddings.create( - input=text, - model="text-embedding-ada-002", - user="user-123" - ) - print(f"Embedding for '{text}': {len(response.data[0].embedding)} dimensions") -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def create_embeddings(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - response = await portkey.embeddings.create( - input="Async embedding creation", - model="text-embedding-ada-002" - ) - - return response.data[0].embedding - -# Run async function -embedding = asyncio.run(create_embeddings()) -``` - -## Types - -```python { .api } -class CreateEmbeddingResponse: - """Response from embedding creation request""" - object: str # "list" - data: List[EmbeddingObject] - model: str - usage: EmbeddingUsage - _headers: Optional[dict] # Response headers for debugging - -class EmbeddingObject: - """Individual embedding object""" - object: str # "embedding" - embedding: List[float] # The embedding vector - index: int # Index in the input list - -class EmbeddingUsage: - """Token usage information""" - prompt_tokens: int - total_tokens: int -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md deleted file mode 100644 index fce371c03..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/evaluation-testing.md +++ /dev/null @@ -1,22 +0,0 @@ -# Evaluation & Testing - -AI evaluation framework with automated grading, test execution, and performance measurement capabilities. - -## Capabilities - -```python { .api } -class Evals: - runs: EvalsRuns - -class EvalsRuns: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Alpha: - graders: Graders - -class Graders: - def create(self, **kwargs): ... - def list(self, **kwargs): ... -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md deleted file mode 100644 index b7fe03168..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/feedback-collections.md +++ /dev/null @@ -1,19 +0,0 @@ -# Feedback & Collections - -User feedback collection and data organization capabilities for improving AI applications and gathering insights. - -## Capabilities - -```python { .api } -class Feedback: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Collections: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md deleted file mode 100644 index 57b6af64a..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/file-management.md +++ /dev/null @@ -1,45 +0,0 @@ -# File Management - -File upload, management, and processing capabilities including support for assistants, fine-tuning, and batch operations. - -## Capabilities - -### File Operations - -```python { .api } -class MainFiles: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def delete(self, **kwargs): ... - -class Uploads: - def create(self, **kwargs): ... - parts: Parts - -class Parts: - def create(self, **kwargs): ... -``` - -## Usage Examples - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Upload file -file_response = portkey.files.create( - file=open("document.pdf", "rb"), - purpose="assistants" -) - -# List files -files = portkey.files.list() - -# Delete file -portkey.files.delete(file_response.id) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md deleted file mode 100644 index 70cf2110d..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/fine-tuning.md +++ /dev/null @@ -1,34 +0,0 @@ -# Fine-Tuning - -Model fine-tuning capabilities with job management, checkpoint handling, and training monitoring. - -## Capabilities - -```python { .api } -class FineTuning: - jobs: Jobs - checkpoints: Checkpoints - -class Jobs: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def cancel(self, **kwargs): ... - -class Checkpoints: - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... -``` - -## Usage Examples - -```python -# Create fine-tuning job -job = portkey.fine_tuning.jobs.create( - training_file="file-123", - model="gpt-3.5-turbo" -) - -# Monitor job -job_status = portkey.fine_tuning.jobs.retrieve(job.id) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md deleted file mode 100644 index 1a2e7ebbd..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/framework-integrations.md +++ /dev/null @@ -1,60 +0,0 @@ -# Framework Integrations - -Pre-built integrations with popular AI frameworks including LangChain and LlamaIndex callback handlers. - -## Capabilities - -### LangChain Integration - -```python { .api } -from portkey_ai.langchain import PortkeyLangchainCallbackHandler - -class PortkeyLangchainCallbackHandler: - """Callback handler for LangChain integration with Portkey observability""" - def __init__( - self, - api_key: Optional[str] = None, - base_url: Optional[str] = None, - **kwargs - ): ... -``` - -### LlamaIndex Integration - -```python { .api } -from portkey_ai.llamaindex import PortkeyLlamaCallbackHandler - -class PortkeyLlamaCallbackHandler: - """Callback handler for LlamaIndex integration with Portkey observability""" - def __init__( - self, - api_key: Optional[str] = None, - base_url: Optional[str] = None, - **kwargs - ): ... -``` - -## Usage Examples - -```python -# LangChain integration -from langchain.llms import OpenAI -from portkey_ai.langchain import PortkeyLangchainCallbackHandler - -handler = PortkeyLangchainCallbackHandler( - api_key="PORTKEY_API_KEY" -) - -llm = OpenAI(callbacks=[handler]) -response = llm("What is AI?") - -# LlamaIndex integration -from llama_index.core import SimpleDirectoryReader -from portkey_ai.llamaindex import PortkeyLlamaCallbackHandler - -handler = PortkeyLlamaCallbackHandler( - api_key="PORTKEY_API_KEY" -) - -# Use with LlamaIndex operations -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/index.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/index.md deleted file mode 100644 index 41f6bf36a..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/index.md +++ /dev/null @@ -1,714 +0,0 @@ -# Portkey AI - -Python client library for the Portkey API - Control Panel for AI Apps. Built on top of the OpenAI SDK, allowing seamless integration of Portkey's advanced features including AI Gateway capabilities (unified API signature, automated fallbacks & retries, load balancing, semantic caching, virtual keys), and comprehensive observability features (logging, request tracing, custom metadata, feedback collection, analytics). - -## Package Information - -- **Package Name**: portkey-ai -- **Language**: Python -- **Installation**: `pip install portkey-ai` - -## Core Imports - -```python -from portkey_ai import Portkey, AsyncPortkey -``` - -For OpenAI compatibility: - -```python -from portkey_ai import openai -``` - -For specific components: - -```python -from portkey_ai import ( - Config, - LLMOptions, - Modes, - ProviderTypes, - createHeaders -) -``` - -## Basic Usage - -```python -from portkey_ai import Portkey - -# Initialize client with API key and virtual key -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Make a chat completion request -chat_completion = portkey.chat.completions.create( - messages=[{"role": "user", "content": "Say this is a test"}], - model="gpt-4" -) - -print(chat_completion) -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def main(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - chat_completion = await portkey.chat.completions.create( - messages=[{"role": "user", "content": "Say this is a test"}], - model="gpt-4" - ) - - print(chat_completion) - -asyncio.run(main()) -``` - -## Architecture - -Portkey's architecture centers around two main client classes that provide comprehensive AI API management: - -- **Portkey/AsyncPortkey**: Main client classes providing unified interface to 40+ AI providers -- **API Resources**: Organized into functional areas (chat, completions, images, audio, etc.) -- **Configuration System**: Flexible config management for providers, fallbacks, retries, and routing -- **Observability Layer**: Built-in logging, tracing, analytics, and feedback collection -- **Security Layer**: Virtual key management and secure credential handling - -The dual client pattern ensures both synchronous and asynchronous support across all operations, while maintaining full OpenAI SDK compatibility. - -## Capabilities - -### Core Client & Configuration - -Primary client classes and configuration utilities for initializing and managing Portkey connections with comprehensive provider support and advanced routing capabilities. - -```python { .api } -class Portkey: - def __init__( - self, - *, - api_key: Optional[str] = None, - base_url: Optional[str] = None, - virtual_key: Optional[str] = None, - config: Optional[Union[Mapping, str]] = None, - provider: Optional[str] = None, - trace_id: Optional[str] = None, - metadata: Union[Optional[dict[str, str]], str] = None, - **kwargs - ) -> None: ... - -class AsyncPortkey: - def __init__(self, **kwargs) -> None: ... - -def createHeaders(**kwargs) -> dict: ... -``` - -[Core Client & Configuration](./core-client.md) - -### Chat Completions - -OpenAI-compatible chat completion API with support for all major providers, streaming, function calling, and advanced Portkey features like fallbacks and load balancing. - -```python { .api } -class ChatCompletion: - completions: Completions - -class Completions: - def create( - self, - *, - messages: Iterable[dict], - model: Optional[str] = "portkey-default", - **kwargs - ) -> Union[ChatCompletions, Iterator[ChatCompletionChunk]]: ... - - messages: ChatCompletionsMessages - -class AsyncChatCompletion: - completions: AsyncCompletions -``` - -[Chat Completions](./chat-completions.md) - -### Text Completions - -Legacy text completion API for non-chat models, supporting streaming and all OpenAI-compatible parameters with Portkey enhancements. - -```python { .api } -class Completion: - def create( - self, - *, - prompt: Union[str, List[str]], - model: str, - **kwargs - ) -> Union[TextCompletion, Iterator[TextCompletionChunk]]: ... - -class AsyncCompletion: - async def create( - self, - *, - prompt: Union[str, List[str]], - model: str, - **kwargs - ) -> Union[TextCompletion, AsyncIterator[TextCompletionChunk]]: ... -``` - -[Text Completions](./text-completions.md) - -### Prompt Management - -Advanced prompt management system with templating, rendering, versioning, and prompt execution capabilities. - -```python { .api } -class Prompts: - def render(self, **kwargs): ... - def completions(self, **kwargs): ... - -class AsyncPrompts: - async def render(self, **kwargs): ... - async def completions(self, **kwargs): ... - -class Generations: - def create(self, **kwargs): ... - -class AsyncGenerations: - async def create(self, **kwargs): ... -``` - -[Prompt Management](./prompt-management.md) - -### Embeddings - -Text embedding generation for semantic similarity, search, and machine learning applications with support for multiple providers. - -```python { .api } -class Embeddings: - def create( - self, - *, - input: str, - model: Optional[str] = "portkey-default", - dimensions: Union[int, NotGiven] = NOT_GIVEN, - encoding_format: Union[str, NotGiven] = NOT_GIVEN, - user: Union[str, NotGiven] = NOT_GIVEN, - **kwargs - ) -> CreateEmbeddingResponse: ... - -class AsyncEmbeddings: - async def create(self, **kwargs) -> CreateEmbeddingResponse: ... -``` - -[Embeddings](./embeddings.md) - -### Models - -Model management and information retrieval for accessing available AI models across different providers. - -```python { .api } -class Models: - def list(self, **kwargs) -> ModelList: ... - def retrieve( - self, - model: str, - *, - timeout: Union[float, NotGiven] = NOT_GIVEN, - **kwargs - ) -> Model: ... - def delete( - self, - model: str, - *, - timeout: Union[float, NotGiven] = NOT_GIVEN, - **kwargs - ) -> ModelDeleted: ... - -class AsyncModels: - async def list(self, **kwargs) -> ModelList: ... - async def retrieve(self, model: str, **kwargs) -> Model: ... - async def delete(self, model: str, **kwargs) -> ModelDeleted: ... -``` - -[Models](./models.md) - -### Multimodal APIs - -Image generation, audio processing (speech-to-text, text-to-speech, translation), and content moderation capabilities with OpenAI compatibility. - -```python { .api } -class Images: - def generate(self, **kwargs): ... - def create_variation(self, **kwargs): ... - def edit(self, **kwargs): ... - -class Audio: - transcriptions: Transcriptions - translations: Translations - speech: Speech - -class Moderations: - def create(self, **kwargs): ... -``` - -[Multimodal APIs](./multimodal-apis.md) - -### File Management - -File upload, management, and processing capabilities including support for assistants, fine-tuning, and batch operations. - -```python { .api } -class MainFiles: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def delete(self, **kwargs): ... - -class Uploads: - def create(self, **kwargs): ... - parts: Parts -``` - -[File Management](./file-management.md) - -### Assistants & Threads - -OpenAI Assistants API implementation with thread management, message handling, and tool execution support. - -```python { .api } -class Assistants: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - -class Threads: - def create(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - messages: Messages - runs: Runs -``` - -[Assistants & Threads](./assistants-threads.md) - -### Vector Stores - -Vector database operations for retrieval-augmented generation (RAG) applications with file management and batch processing. - -```python { .api } -class VectorStores: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - files: VectorFiles - file_batches: VectorFileBatches -``` - -[Vector Stores](./vector-stores.md) - -### Fine-Tuning - -Model fine-tuning capabilities with job management, checkpoint handling, and training monitoring. - -```python { .api } -class FineTuning: - jobs: Jobs - checkpoints: Checkpoints - -class Jobs: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def cancel(self, **kwargs): ... -``` - -[Fine-Tuning](./fine-tuning.md) - -### Batch Processing - -Batch API for processing large volumes of requests efficiently with cost optimization and result management. - -```python { .api } -class Batches: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def cancel(self, **kwargs): ... -``` - -[Batch Processing](./batch-processing.md) - -### Administration & User Management - -Administrative functions for managing users, workspaces, invitations, and organizational settings. - -```python { .api } -class Admin: - users: Users - invites: Invites - workspaces: Workspaces - -class Users: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` - -[Administration & User Management](./administration.md) - -### Configuration Management - -Advanced configuration system for managing provider settings, routing rules, fallback strategies, and load balancing configurations. - -```python { .api } -class Configs: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` - -[Configuration Management](./configuration-management.md) - -### API Key & Virtual Key Management - -Secure API key and virtual key management for credential protection and access control. - -```python { .api } -class ApiKeys: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - -class VirtualKeys: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` - -[API Key & Virtual Key Management](./key-management.md) - -### Observability & Analytics - -Comprehensive logging, request tracing, analytics, and monitoring capabilities with custom metadata and performance metrics. - -```python { .api } -class Logs: - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Responses: - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - input_items: InputItems - output_items: OutputItems - -class Labels: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` - -[Observability & Analytics](./observability-analytics.md) - -### Feedback & Collections - -User feedback collection and data organization capabilities for improving AI applications and gathering insights. - -```python { .api } -class Feedback: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Collections: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` - -[Feedback & Collections](./feedback-collections.md) - -### Evaluation & Testing - -AI evaluation framework with automated grading, test execution, and performance measurement capabilities. - -```python { .api } -class Evals: - runs: EvalsRuns - -class EvalsRuns: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Alpha: - graders: Graders - -class Graders: - def create(self, **kwargs): ... - def list(self, **kwargs): ... -``` - -[Evaluation & Testing](./evaluation-testing.md) - -### Container & Content Management - -Container-based deployment and content management capabilities for scalable AI applications. - -```python { .api } -class Containers: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - files: ContainersFiles - -class Content: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... -``` - -[Container & Content Management](./container-content.md) - -### Large File Uploads - -Multi-part upload system for large files with chunked upload support and integrity verification. - -```python { .api } -class Uploads: - def create( - self, - *, - bytes: int, - filename: str, - mime_type: str, - purpose: Any, - **kwargs - ) -> Upload: ... - - def upload_file_chunked( - self, - *, - file: Union[os.PathLike[str], bytes], - mime_type: str, - purpose: Any, - **kwargs - ) -> Any: ... - - def complete( - self, - upload_id: str, - *, - part_ids: List[str], - **kwargs - ) -> Upload: ... - - parts: Parts - -class AsyncUploads: - async def create(self, **kwargs) -> Upload: ... - async def upload_file_chunked(self, **kwargs) -> Any: ... - async def complete(self, upload_id: str, **kwargs) -> Upload: ... - parts: AsyncParts -``` - -[Large File Uploads](./uploads.md) - -### Beta Realtime API - -Real-time audio and WebSocket-based AI interactions for building conversational applications with low-latency voice communication. - -```python { .api } -class BetaRealtime: - def connect( - self, - *, - model: str, - websocket_connection_options: WebsocketConnectionOptions = {}, - **kwargs - ) -> RealtimeConnectionManager: ... - - sessions: BetaSessions - -class BetaSessions: - def create( - self, - *, - model: Any = "portkey-default", - modalities: Union[List[Any], NotGiven] = NOT_GIVEN, - voice: Union[Any, NotGiven] = NOT_GIVEN, - instructions: Union[str, NotGiven] = NOT_GIVEN, - **kwargs - ) -> SessionCreateResponse: ... - -class AsyncBetaRealtime: - def connect(self, **kwargs) -> AsyncRealtimeConnectionManager: ... - sessions: AsyncBetaSessions -``` - -[Beta Realtime API](./beta-realtime.md) - -### Provider & Integration Management - -Management of AI providers and third-party integrations with workspace-level and model-level configuration. - -```python { .api } -class Providers: - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Integrations: - workspaces: IntegrationsWorkspaces - models: IntegrationsModels - -class IntegrationsWorkspaces: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... -``` - -[Provider & Integration Management](./provider-integration.md) - -### Framework Integrations - -Pre-built integrations with popular AI frameworks including LangChain and LlamaIndex callback handlers. - -```python { .api } -from portkey_ai.langchain import PortkeyLangchainCallbackHandler -from portkey_ai.llamaindex import PortkeyLlamaCallbackHandler -``` - -[Framework Integrations](./framework-integrations.md) - -## Types - -### Core Configuration Types - -```python { .api } -class Config(TypedDict): - """Configuration object for Portkey requests""" - ... - -class LLMOptions(TypedDict): - """LLM-specific options and parameters""" - ... - -class Modes(Enum): - """Available Portkey modes""" - SINGLE = "single" - FALLBACK = "fallback" - LOADBALANCE = "loadbalance" - AB_TEST = "ab_test" - -class ProviderTypes(Enum): - """Supported AI providers""" - OPENAI = "openai" - ANTHROPIC = "anthropic" - AZURE_OPENAI = "azure-openai" - COHERE = "cohere" - HUGGINGFACE = "huggingface" - # ... and 35+ more providers - -class RetrySettings(TypedDict): - """Retry configuration settings""" - attempts: int - on_status_codes: List[int] -``` - -### Response Types - -```python { .api } -class PortkeyResponse: - """Base response class for all Portkey operations""" - ... - -class ChatCompletions: - """Chat completion response""" - id: str - object: str - created: int - model: str - choices: List[dict] - usage: dict - -class ChatCompletionChunk: - """Streaming chat completion chunk""" - id: str - object: str - created: int - model: str - choices: List[dict] - -class TextCompletion: - """Text completion response""" - id: str - object: str - created: int - model: str - choices: List[dict] - usage: dict - -class Message: - """Message object for chat completions""" - role: str - content: str -``` - -### Exception Types - -```python { .api } -class APIStatusError(Exception): - """Base exception for API status errors""" - ... - -class BadRequestError(APIStatusError): - """400 Bad Request error""" - ... - -class AuthenticationError(APIStatusError): - """401 Authentication error""" - ... - -class PermissionDeniedError(APIStatusError): - """403 Permission denied error""" - ... - -class NotFoundError(APIStatusError): - """404 Not found error""" - ... - -class RateLimitError(APIStatusError): - """429 Rate limit error""" - ... - -class InternalServerError(APIStatusError): - """500 Internal server error""" - ... -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md deleted file mode 100644 index 68a826589..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/key-management.md +++ /dev/null @@ -1,21 +0,0 @@ -# API Key & Virtual Key Management - -Secure API key and virtual key management for credential protection and access control. - -## Capabilities - -```python { .api } -class ApiKeys: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - -class VirtualKeys: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/models.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/models.md deleted file mode 100644 index d1dc28678..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/models.md +++ /dev/null @@ -1,206 +0,0 @@ -# Models - -Model management and information retrieval for accessing available AI models across different providers. Provides unified interface to list, retrieve details, and manage models from various AI providers. - -## Capabilities - -### Model Listing - -Retrieves list of available models from the configured provider(s), including model capabilities, permissions, and availability status. - -```python { .api } -class Models: - def list(self, **kwargs) -> ModelList: - """ - List available models from the configured provider. - - Args: - **kwargs: Additional provider-specific parameters - - Returns: - ModelList: List of available models with metadata - """ - - def retrieve( - self, - model: str, - *, - timeout: Union[float, NotGiven] = NOT_GIVEN, - **kwargs - ) -> Model: - """ - Retrieve detailed information about a specific model. - - Args: - model: Model identifier (e.g., "gpt-4", "claude-3-opus") - timeout: Request timeout in seconds - **kwargs: Additional provider-specific parameters - - Returns: - Model: Detailed model information and capabilities - """ - - def delete( - self, - model: str, - *, - timeout: Union[float, NotGiven] = NOT_GIVEN, - **kwargs - ) -> ModelDeleted: - """ - Delete a custom model (if supported by provider). - - Args: - model: Model identifier to delete - timeout: Request timeout in seconds - **kwargs: Additional provider-specific parameters - - Returns: - ModelDeleted: Confirmation of model deletion - """ - -class AsyncModels: - async def list(self, **kwargs) -> ModelList: - """Async version of list method.""" - - async def retrieve( - self, - model: str, - *, - timeout: Union[float, NotGiven] = NOT_GIVEN, - **kwargs - ) -> Model: - """Async version of retrieve method.""" - - async def delete( - self, - model: str, - *, - timeout: Union[float, NotGiven] = NOT_GIVEN, - **kwargs - ) -> ModelDeleted: - """Async version of delete method.""" -``` - -### Usage Examples - -```python -from portkey_ai import Portkey - -# Initialize client -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# List all available models -models = portkey.models.list() -print(f"Available models: {len(models.data)}") - -for model in models.data: - print(f"- {model.id}: {model.owned_by}") - -# Get details for a specific model -model_info = portkey.models.retrieve("gpt-4") -print(f"Model: {model_info.id}") -print(f"Created: {model_info.created}") -print(f"Owned by: {model_info.owned_by}") -print(f"Permissions: {model_info.permission}") - -# Filter models by provider (provider-specific) -openai_models = portkey.models.list(provider="openai") -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def list_models(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - # List models asynchronously - models = await portkey.models.list() - - # Get model details - model_details = await portkey.models.retrieve("gpt-4") - - return models, model_details - -models, details = asyncio.run(list_models()) -print(f"Found {len(models.data)} models") -print(f"GPT-4 details: {details.id}") -``` - -### Multi-Provider Model Discovery - -```python -# List models across multiple providers using Portkey configuration -config = { - "mode": "fallback", - "options": [ - {"provider": "openai", "api_key": "openai_key"}, - {"provider": "anthropic", "api_key": "anthropic_key"}, - {"provider": "cohere", "api_key": "cohere_key"} - ] -} - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - config=config -) - -# This will list models from the primary provider in the fallback chain -all_models = portkey.models.list() - -# Access models by capability -chat_models = [m for m in all_models.data if "chat" in m.id.lower()] -completion_models = [m for m in all_models.data if "text" in m.id.lower()] -``` - -## Types - -```python { .api } -class ModelList: - """List of available models""" - object: str # "list" - data: List[Model] - _headers: Optional[dict] # Response headers - -class Model: - """Individual model information""" - id: str # Model identifier - object: str # "model" - created: int # Unix timestamp of creation - owned_by: str # Organization that owns the model - permission: List[ModelPermission] # Model permissions - root: Optional[str] # Root model identifier - parent: Optional[str] # Parent model identifier - _headers: Optional[dict] # Response headers - -class ModelPermission: - """Model permission details""" - id: str - object: str # "model_permission" - created: int - allow_create_engine: bool - allow_sampling: bool - allow_logprobs: bool - allow_search_indices: bool - allow_view: bool - allow_fine_tuning: bool - organization: str - group: Optional[str] - is_blocking: bool - -class ModelDeleted: - """Model deletion confirmation""" - id: str # Deleted model identifier - object: str # "model" - deleted: bool # True if successfully deleted - _headers: Optional[dict] # Response headers -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md deleted file mode 100644 index 902587257..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/multimodal-apis.md +++ /dev/null @@ -1,68 +0,0 @@ -# Multimodal APIs - -Image generation, audio processing (speech-to-text, text-to-speech, translation), and content moderation capabilities with OpenAI compatibility. - -## Capabilities - -### Image Generation - -```python { .api } -class Images: - def generate(self, **kwargs): ... - def create_variation(self, **kwargs): ... - def edit(self, **kwargs): ... -``` - -### Audio Processing - -```python { .api } -class Audio: - transcriptions: Transcriptions - translations: Translations - speech: Speech - -class Transcriptions: - def create(self, **kwargs): ... - -class Translations: - def create(self, **kwargs): ... - -class Speech: - def create(self, **kwargs): ... -``` - -### Content Moderation - -```python { .api } -class Moderations: - def create(self, **kwargs): ... -``` - -## Usage Examples - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Generate image -image = portkey.images.generate( - prompt="A cat in a spacesuit", - model="dall-e-3", - size="1024x1024" -) - -# Transcribe audio -transcription = portkey.audio.transcriptions.create( - file=open("audio.mp3", "rb"), - model="whisper-1" -) - -# Moderate content -moderation = portkey.moderations.create( - input="This is a test message" -) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md deleted file mode 100644 index a9706cd65..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/observability-analytics.md +++ /dev/null @@ -1,528 +0,0 @@ -# Observability & Analytics - -Comprehensive logging, request tracing, analytics, and monitoring capabilities with custom metadata and performance metrics. - -## Capabilities - -### Request Logging - -Track and analyze all API requests with detailed logging capabilities including request/response data, performance metrics, and custom metadata. - -```python { .api } -class Logs: - """Request logging and retrieval""" - - def list( - self, - *, - limit: Optional[int] = None, - after: Optional[str] = None, - before: Optional[str] = None, - order: Optional[str] = None, - filters: Optional[dict] = None, - **kwargs - ) -> dict: - """ - List request logs with filtering and pagination. - - Parameters: - - limit: Maximum number of logs to retrieve - - after: Cursor for pagination (logs after this ID) - - before: Cursor for pagination (logs before this ID) - - order: Sort order ('asc' or 'desc') - - filters: Filter criteria (date range, model, provider, etc.) - - Returns: - Dictionary containing log entries and pagination info - """ - - def retrieve( - self, - log_id: str, - **kwargs - ) -> dict: - """ - Retrieve a specific log entry by ID. - - Parameters: - - log_id: Unique identifier for the log entry - - Returns: - Detailed log entry with request/response data - """ - -class AsyncLogs: - """Async request logging and retrieval""" - - async def list(self, **kwargs) -> dict: ... - async def retrieve(self, log_id: str, **kwargs) -> dict: ... -``` - -### Response Analysis - -Analyze API responses with detailed breakdowns of input/output tokens, performance metrics, and content analysis. - -```python { .api } -class Responses: - """Response analysis and metrics""" - - def list( - self, - *, - limit: Optional[int] = None, - after: Optional[str] = None, - before: Optional[str] = None, - filters: Optional[dict] = None, - **kwargs - ) -> dict: - """List and analyze API responses""" - - def retrieve( - self, - response_id: str, - **kwargs - ) -> dict: - """Retrieve detailed response analysis""" - - input_items: InputItems - output_items: OutputItems - -class InputItems: - """Input content analysis""" - - def list(self, **kwargs) -> dict: ... - def retrieve(self, item_id: str, **kwargs) -> dict: ... - -class OutputItems: - """Output content analysis""" - - def list(self, **kwargs) -> dict: ... - def retrieve(self, item_id: str, **kwargs) -> dict: ... - -class AsyncResponses: - """Async response analysis""" - - async def list(self, **kwargs) -> dict: ... - async def retrieve(self, response_id: str, **kwargs) -> dict: ... - input_items: AsyncInputItems - output_items: AsyncOutputItems -``` - -### Request Labeling - -Categorize and tag API requests for better organization, analysis, and filtering. - -```python { .api } -class Labels: - """Request labeling and categorization""" - - def create( - self, - *, - log_id: str, - label: str, - value: Optional[str] = None, - **kwargs - ) -> dict: - """ - Create a label for a request log. - - Parameters: - - log_id: ID of the log entry to label - - label: Label name/category - - value: Optional label value - - Returns: - Created label object - """ - - def list( - self, - *, - log_id: Optional[str] = None, - label: Optional[str] = None, - **kwargs - ) -> dict: - """List labels with optional filtering""" - - def update( - self, - label_id: str, - *, - value: Optional[str] = None, - **kwargs - ) -> dict: - """Update an existing label""" - - def delete( - self, - label_id: str, - **kwargs - ) -> dict: - """Delete a label""" - -class AsyncLabels: - """Async request labeling""" - - async def create(self, **kwargs) -> dict: ... - async def list(self, **kwargs) -> dict: ... - async def update(self, label_id: str, **kwargs) -> dict: ... - async def delete(self, label_id: str, **kwargs) -> dict: ... -``` - -## Usage Examples - -### Basic Request Logging - -```python -from portkey_ai import Portkey - -# Initialize with observability enabled -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY", - debug=True, # Enable detailed logging - metadata={ - "environment": "production", - "user_id": "user123", - "session_id": "session456" - } -) - -# Make a request (automatically logged) -response = portkey.chat.completions.create( - messages=[{"role": "user", "content": "Hello"}], - model="gpt-4", - metadata={ - "request_type": "greeting", - "category": "customer_support" - } -) - -# Retrieve recent logs -logs = portkey.logs.list(limit=10, order="desc") -print(f"Found {len(logs['data'])} recent requests") - -for log in logs['data']: - print(f"Request {log['id']}: {log['model']} - {log['status']}") - print(f" Tokens: {log['usage']['total_tokens']}") - print(f" Duration: {log['duration_ms']}ms") -``` - -### Advanced Log Filtering - -```python -from datetime import datetime, timedelta - -# Filter logs by date range and criteria -yesterday = datetime.now() - timedelta(days=1) -logs = portkey.logs.list( - filters={ - "date": { - "gte": yesterday.isoformat(), - "lte": datetime.now().isoformat() - }, - "model": "gpt-4", - "status": "success", - "provider": "openai", - "metadata.environment": "production" - }, - limit=50 -) - -# Analyze request patterns -total_requests = len(logs['data']) -total_tokens = sum(log['usage']['total_tokens'] for log in logs['data']) -avg_latency = sum(log['duration_ms'] for log in logs['data']) / total_requests - -print(f"Analysis for last 24 hours:") -print(f" Total requests: {total_requests}") -print(f" Total tokens: {total_tokens}") -print(f" Average latency: {avg_latency:.2f}ms") -``` - -### Request Tracing - -```python -import uuid - -# Use trace IDs to track request flows -trace_id = str(uuid.uuid4()) - -# First request in trace -response1 = portkey.chat.completions.create( - messages=[{"role": "user", "content": "What is Python?"}], - model="gpt-4", - trace_id=trace_id, - metadata={"step": "initial_query"} -) - -# Follow-up request in same trace -response2 = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "What is Python?"}, - {"role": "assistant", "content": response1.choices[0].message.content}, - {"role": "user", "content": "Give me a code example"} - ], - model="gpt-4", - trace_id=trace_id, - metadata={"step": "follow_up"} -) - -# Retrieve all logs for this trace -trace_logs = portkey.logs.list( - filters={"trace_id": trace_id} -) - -print(f"Trace {trace_id} has {len(trace_logs['data'])} requests") -``` - -### Response Analysis - -```python -# Analyze response patterns and content -responses = portkey.responses.list( - filters={ - "model": "gpt-4", - "date": {"gte": "2024-01-01"} - }, - limit=100 -) - -# Analyze input/output patterns -for response in responses['data']: - response_detail = portkey.responses.retrieve(response['id']) - - print(f"Response {response['id']}:") - print(f" Input tokens: {response_detail['input_tokens']}") - print(f" Output tokens: {response_detail['output_tokens']}") - print(f" Cost: ${response_detail['cost']:.4f}") - - # Analyze input items - input_items = portkey.responses.input_items.list( - response_id=response['id'] - ) - - # Analyze output items - output_items = portkey.responses.output_items.list( - response_id=response['id'] - ) -``` - -### Request Labeling and Categorization - -```python -# Create labels for request categorization -recent_logs = portkey.logs.list(limit=20) - -for log in recent_logs['data']: - # Auto-label based on content - if 'translate' in log['request']['messages'][0]['content'].lower(): - portkey.labels.create( - log_id=log['id'], - label="category", - value="translation" - ) - elif 'code' in log['request']['messages'][0]['content'].lower(): - portkey.labels.create( - log_id=log['id'], - label="category", - value="coding" - ) - - # Label by performance - if log['duration_ms'] > 5000: - portkey.labels.create( - log_id=log['id'], - label="performance", - value="slow" - ) - -# Query logs by labels -coding_requests = portkey.logs.list( - filters={"labels.category": "coding"} -) - -slow_requests = portkey.logs.list( - filters={"labels.performance": "slow"} -) - -print(f"Found {len(coding_requests['data'])} coding requests") -print(f"Found {len(slow_requests['data'])} slow requests") -``` - -### Real-time Monitoring - -```python -import time - -def monitor_requests(): - """Monitor requests in real-time""" - last_check = datetime.now() - - while True: - # Get new logs since last check - new_logs = portkey.logs.list( - filters={ - "date": {"gte": last_check.isoformat()} - }, - order="asc" - ) - - for log in new_logs['data']: - print(f"New request: {log['id']}") - print(f" Model: {log['model']}") - print(f" Status: {log['status']}") - print(f" Duration: {log['duration_ms']}ms") - print(f" Tokens: {log['usage']['total_tokens']}") - - # Alert on errors - if log['status'] == 'error': - print(f" ERROR: {log['error']['message']}") - - # Alert on slow requests - if log['duration_ms'] > 10000: - print(f" WARNING: Slow request ({log['duration_ms']}ms)") - - last_check = datetime.now() - time.sleep(30) # Check every 30 seconds - -# Run monitoring (in production, use proper async/threading) -# monitor_requests() -``` - -### Custom Analytics Dashboard - -```python -from collections import defaultdict - -def generate_analytics_report(days=7): - """Generate comprehensive analytics report""" - - # Get logs for specified period - start_date = datetime.now() - timedelta(days=days) - logs = portkey.logs.list( - filters={ - "date": {"gte": start_date.isoformat()} - }, - limit=1000 - ) - - # Aggregate metrics - metrics = { - 'total_requests': len(logs['data']), - 'by_model': defaultdict(int), - 'by_provider': defaultdict(int), - 'by_status': defaultdict(int), - 'total_tokens': 0, - 'total_cost': 0, - 'avg_latency': 0, - 'error_rate': 0 - } - - latencies = [] - errors = 0 - - for log in logs['data']: - metrics['by_model'][log['model']] += 1 - metrics['by_provider'][log['provider']] += 1 - metrics['by_status'][log['status']] += 1 - metrics['total_tokens'] += log['usage']['total_tokens'] - metrics['total_cost'] += log['cost'] - latencies.append(log['duration_ms']) - - if log['status'] == 'error': - errors += 1 - - metrics['avg_latency'] = sum(latencies) / len(latencies) if latencies else 0 - metrics['error_rate'] = (errors / len(logs['data'])) * 100 if logs['data'] else 0 - - # Print report - print(f"Analytics Report ({days} days)") - print("=" * 40) - print(f"Total Requests: {metrics['total_requests']}") - print(f"Total Tokens: {metrics['total_tokens']:,}") - print(f"Total Cost: ${metrics['total_cost']:.2f}") - print(f"Average Latency: {metrics['avg_latency']:.0f}ms") - print(f"Error Rate: {metrics['error_rate']:.2f}%") - - print("\nTop Models:") - for model, count in sorted(metrics['by_model'].items(), key=lambda x: x[1], reverse=True)[:5]: - print(f" {model}: {count} requests") - - print("\nProviders:") - for provider, count in metrics['by_provider'].items(): - print(f" {provider}: {count} requests") - - return metrics - -# Generate report -analytics = generate_analytics_report(days=30) -``` - -### Async Observability Operations - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def async_observability_example(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - # Make async request with metadata - response = await portkey.chat.completions.create( - messages=[{"role": "user", "content": "Hello async world"}], - model="gpt-4", - metadata={"async": "true", "test": "observability"} - ) - - # Async log retrieval - logs = await portkey.logs.list(limit=5) - - # Async labeling - if logs['data']: - await portkey.labels.create( - log_id=logs['data'][0]['id'], - label="async_test", - value="true" - ) - - # Async response analysis - responses = await portkey.responses.list(limit=5) - for resp in responses['data']: - detail = await portkey.responses.retrieve(resp['id']) - print(f"Response {resp['id']}: {detail['input_tokens']} → {detail['output_tokens']} tokens") - -asyncio.run(async_observability_example()) -``` - -## Analytics Metrics - -Portkey automatically tracks 40+ production-critical metrics including: - -### Performance Metrics -- Request latency (p50, p95, p99) -- Throughput (requests per minute/hour) -- Error rates by provider/model -- Token usage and costs - -### Usage Metrics -- Requests by model, provider, user -- Token consumption patterns -- Cost analysis and optimization -- Geographic distribution - -### Quality Metrics -- Response quality scores -- User feedback integration -- A/B test results -- Conversion tracking - -### Infrastructure Metrics -- Cache hit rates -- Fallback trigger frequency -- Load balancing distribution -- Provider availability \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md deleted file mode 100644 index b415c8295..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/prompt-management.md +++ /dev/null @@ -1,254 +0,0 @@ -# Prompt Management - -Advanced prompt management system with templating, rendering, versioning, and prompt execution capabilities. - -## Capabilities - -### Prompt Rendering and Execution - -Advanced prompt management with templating, versioning, and execution capabilities. - -```python { .api } -class Prompts: - def render( - self, - *, - prompt_id: str, - variables: Mapping[str, Any] - ) -> PromptRender: - """ - Render a prompt template with variables. - - Args: - prompt_id: Unique identifier for the prompt template - variables: Dictionary of variables to substitute in template - - Returns: - PromptRender: Rendered prompt content - """ - - def completions( - self, - *, - prompt_id: str, - variables: Optional[Mapping[str, Any]] = None, - **kwargs - ) -> Union[PromptCompletion, Stream[PromptCompletionChunk]]: - """ - Execute a prompt template and generate completion. - - Args: - prompt_id: Unique identifier for the prompt template - variables: Variables for template substitution - **kwargs: Additional completion parameters (model, temperature, etc.) - - Returns: - PromptCompletion: Completion response or streaming chunks - """ - - completions: Completions - versions: PromptVersions - partials: PromptPartials - -class AsyncPrompts: - async def render( - self, - *, - prompt_id: str, - variables: Mapping[str, Any] - ) -> PromptRender: - """Async version of render method.""" - - async def completions( - self, - *, - prompt_id: str, - variables: Optional[Mapping[str, Any]] = None, - **kwargs - ) -> Union[PromptCompletion, AsyncStream[PromptCompletionChunk]]: - """Async version of completions method.""" - - completions: AsyncCompletions - versions: AsyncPromptVersions - partials: AsyncPromptPartials -``` - -### Prompt Versions - -Manage different versions of prompt templates for A/B testing and iterative improvement. - -```python { .api } -class PromptVersions: - def list(self, prompt_id: str, **kwargs): ... - def retrieve(self, prompt_id: str, version_id: str, **kwargs): ... - def create(self, prompt_id: str, **kwargs): ... - def update(self, prompt_id: str, version_id: str, **kwargs): ... - -class AsyncPromptVersions: - async def list(self, prompt_id: str, **kwargs): ... - async def retrieve(self, prompt_id: str, version_id: str, **kwargs): ... - async def create(self, prompt_id: str, **kwargs): ... - async def update(self, prompt_id: str, version_id: str, **kwargs): ... -``` - -### Prompt Partials - -Handle partial prompt rendering and composition for complex prompt structures. - -```python { .api } -class PromptPartials: - def render(self, **kwargs): ... - def create(self, **kwargs): ... - -class AsyncPromptPartials: - async def render(self, **kwargs): ... - async def create(self, **kwargs): ... -``` - -### Generation Management (Deprecated) - -Legacy prompt generation API - use Prompts API instead for new implementations. - -```python { .api } -class Generations: - def create( - self, - *, - prompt_id: str, - config: Optional[Union[Mapping, str]] = None, - variables: Optional[Mapping[str, Any]] = None - ) -> Union[GenericResponse, Stream[GenericResponse]]: - """ - DEPRECATED: Create prompt generation. - Use portkey.prompts.completions() instead. - """ - -class AsyncGenerations: - async def create( - self, - *, - prompt_id: str, - config: Optional[Union[Mapping, str]] = None, - variables: Optional[Mapping[str, Any]] = None - ) -> Union[GenericResponse, AsyncStream[GenericResponse]]: - """DEPRECATED: Async version of create method.""" -``` - -## Usage Examples - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Render prompt template -rendered = portkey.prompts.render( - prompt_id="template_123", - variables={"name": "Alice", "topic": "AI"} -) - -print(f"Rendered prompt: {rendered.prompt}") - -# Execute prompt with completion -response = portkey.prompts.completions( - prompt_id="template_123", - variables={"name": "Alice", "topic": "AI"}, - model="gpt-4", - temperature=0.7, - max_tokens=150 -) - -print(f"Response: {response.choices[0].message.content}") - -# Work with prompt versions -versions = portkey.prompts.versions.list("template_123") -print(f"Available versions: {[v.id for v in versions.data]}") - -# Get specific version -version = portkey.prompts.versions.retrieve("template_123", "v2") -print(f"Version details: {version.prompt}") - -# Stream completion responses -for chunk in portkey.prompts.completions( - prompt_id="template_123", - variables={"name": "Alice", "topic": "AI"}, - model="gpt-4", - stream=True -): - if chunk.choices[0].delta.content: - print(chunk.choices[0].delta.content, end="") -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def manage_prompts(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - # Render prompt asynchronously - rendered = await portkey.prompts.render( - prompt_id="template_123", - variables={"name": "Bob", "topic": "Machine Learning"} - ) - - # Execute prompt completion - response = await portkey.prompts.completions( - prompt_id="template_123", - variables={"name": "Bob", "topic": "Machine Learning"}, - model="gpt-4" - ) - - return rendered, response - -rendered, response = asyncio.run(manage_prompts()) -``` - -## Types - -```python { .api } -class PromptRender: - """Rendered prompt content""" - prompt: str # Rendered prompt text - variables: dict # Variables used in rendering - prompt_id: str # Source prompt template ID - -class PromptCompletion: - """Prompt completion response""" - id: str # Completion ID - object: str # "prompt.completion" - created: int # Unix timestamp - model: str # Model used - choices: List[PromptChoice] # Response choices - usage: dict # Token usage information - prompt_id: str # Source prompt ID - -class PromptCompletionChunk: - """Streaming prompt completion chunk""" - id: str # Completion ID - object: str # "prompt.completion.chunk" - created: int # Unix timestamp - model: str # Model used - choices: List[PromptChoiceDelta] # Response deltas - prompt_id: str # Source prompt ID - -class PromptChoice: - """Prompt completion choice""" - index: int # Choice index - message: dict # Response message - finish_reason: Optional[str] # Completion reason - -class PromptChoiceDelta: - """Streaming choice delta""" - index: int # Choice index - delta: dict # Content delta - finish_reason: Optional[str] # Completion reason -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md deleted file mode 100644 index 646debb26..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/provider-integration.md +++ /dev/null @@ -1,24 +0,0 @@ -# Provider & Integration Management - -Management of AI providers and third-party integrations with workspace-level and model-level configuration. - -## Capabilities - -```python { .api } -class Providers: - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class Integrations: - workspaces: IntegrationsWorkspaces - models: IntegrationsModels - -class IntegrationsWorkspaces: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - -class IntegrationsModels: - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md deleted file mode 100644 index 5e7b2c16c..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/text-completions.md +++ /dev/null @@ -1,354 +0,0 @@ -# Text Completions - -Legacy text completion API for non-chat models, supporting streaming and all OpenAI-compatible parameters with Portkey enhancements. - -## Capabilities - -### Text Completion Creation - -Generate text completions using prompt-based models with support for streaming, multiple completions, and advanced parameters. - -```python { .api } -class Completion: - """Synchronous text completion API""" - - def create( - self, - *, - prompt: Union[str, List[str]], - model: str, - best_of: Optional[int] = None, - echo: Optional[bool] = None, - frequency_penalty: Optional[float] = None, - logit_bias: Optional[dict] = None, - logprobs: Optional[int] = None, - max_tokens: Optional[int] = None, - n: Optional[int] = None, - presence_penalty: Optional[float] = None, - seed: Optional[int] = None, - stop: Union[Optional[str], List[str]] = None, - stream: Optional[bool] = None, - suffix: Optional[str] = None, - temperature: Optional[float] = None, - top_p: Optional[float] = None, - user: Optional[str] = None, - **kwargs - ) -> Union[TextCompletion, Iterator[TextCompletionChunk]]: - """ - Create a text completion. - - Parameters: - - prompt: Text prompt or list of prompts - - model: Model identifier (e.g., 'text-davinci-003', 'gpt-3.5-turbo-instruct') - - max_tokens: Maximum tokens to generate - - temperature: Sampling temperature (0-2) - - top_p: Nucleus sampling parameter (0-1) - - n: Number of completions to generate - - stream: Enable streaming responses - - stop: Stop sequences - - presence_penalty: Presence penalty (-2 to 2) - - frequency_penalty: Frequency penalty (-2 to 2) - - best_of: Generate best_of completions server-side and return the best - - echo: Echo back the prompt in addition to the completion - - logprobs: Include log probabilities on the logprobs most likely tokens - - logit_bias: Modify likelihood of specified tokens - - suffix: Suffix that comes after completion of inserted text - - seed: Seed for deterministic generation - - user: User identifier for tracking - - Returns: - TextCompletion object or Iterator of TextCompletionChunk for streaming - """ - -class AsyncCompletion: - """Asynchronous text completion API""" - - async def create( - self, - *, - prompt: Union[str, List[str]], - model: str, - **kwargs - ) -> Union[TextCompletion, AsyncIterator[TextCompletionChunk]]: - """Async version of text completion creation""" -``` - -## Usage Examples - -### Basic Text Completion - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Simple text completion -response = portkey.completions.create( - prompt="The future of artificial intelligence is", - model="gpt-3.5-turbo-instruct", - max_tokens=100, - temperature=0.7 -) - -print(response.choices[0].text) -``` - -### Multiple Prompts - -```python -# Multiple prompts in one request -prompts = [ - "The capital of France is", - "The largest planet in our solar system is", - "Python is a programming language that" -] - -response = portkey.completions.create( - prompt=prompts, - model="gpt-3.5-turbo-instruct", - max_tokens=50, - temperature=0.5 -) - -for i, choice in enumerate(response.choices): - print(f"Prompt {i+1}: {choice.text.strip()}") -``` - -### Streaming Completion - -```python -# Streaming text completion -stream = portkey.completions.create( - prompt="Write a short story about a robot discovering emotions:", - model="gpt-3.5-turbo-instruct", - max_tokens=200, - stream=True -) - -for chunk in stream: - if chunk.choices[0].text: - print(chunk.choices[0].text, end="") -``` - -### Code Completion with Suffix - -```python -# Code completion with suffix (fill-in-the-middle) -response = portkey.completions.create( - prompt="def calculate_fibonacci(n):\n if n <= 1:\n return n\n else:\n ", - suffix="\n return result", - model="gpt-3.5-turbo-instruct", - max_tokens=100, - temperature=0.2 -) - -print(response.choices[0].text) -``` - -### Advanced Parameters - -```python -# Using advanced parameters -response = portkey.completions.create( - prompt="Explain quantum computing in simple terms:", - model="gpt-3.5-turbo-instruct", - max_tokens=150, - temperature=0.8, - top_p=0.9, - frequency_penalty=0.1, - presence_penalty=0.1, - best_of=3, # Generate 3 completions, return the best - n=1, # Return 1 completion - echo=True, # Include the prompt in the response - logprobs=3, # Include log probabilities for top 3 tokens - stop=["\n\n", "###"], # Stop at double newline or ### - user="user123" -) - -print("Full response with prompt:", response.choices[0].text) -if response.choices[0].logprobs: - print("Token log probabilities:", response.choices[0].logprobs) -``` - -### Async Usage - -```python -import asyncio -from portkey_ai import AsyncPortkey - -async def completion_example(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - # Async completion - response = await portkey.completions.create( - prompt="The benefits of renewable energy include:", - model="gpt-3.5-turbo-instruct", - max_tokens=100 - ) - - print(response.choices[0].text) - - # Async streaming - async for chunk in await portkey.completions.create( - prompt="List the planets in our solar system:", - model="gpt-3.5-turbo-instruct", - max_tokens=100, - stream=True - ): - if chunk.choices[0].text: - print(chunk.choices[0].text, end="") - -asyncio.run(completion_example()) -``` - -### Deterministic Generation - -```python -# Reproducible completions with seed -response1 = portkey.completions.create( - prompt="Generate a random story idea:", - model="gpt-3.5-turbo-instruct", - max_tokens=50, - temperature=0.9, - seed=42 -) - -response2 = portkey.completions.create( - prompt="Generate a random story idea:", - model="gpt-3.5-turbo-instruct", - max_tokens=50, - temperature=0.9, - seed=42 -) - -# These should be identical -print("Response 1:", response1.choices[0].text) -print("Response 2:", response2.choices[0].text) -``` - -### Logit Bias Control - -```python -# Modify token probabilities with logit bias -response = portkey.completions.create( - prompt="The weather today is", - model="gpt-3.5-turbo-instruct", - max_tokens=20, - logit_bias={ - # Increase likelihood of positive weather words - 1180: 2, # "sunny" - 4771: 2, # "beautiful" - # Decrease likelihood of negative weather words - 21281: -2, # "rainy" - 4172: -2 # "cloudy" - } -) - -print(response.choices[0].text) -``` - -## Model Compatibility - -### Supported Models - -Text completions work with various model types: - -```python -# GPT-3.5 Instruct models -model = "gpt-3.5-turbo-instruct" - -# Legacy GPT-3 models (deprecated) -model = "text-davinci-003" -model = "text-curie-001" -model = "text-babbage-001" -model = "text-ada-001" - -# Code-specific models -model = "code-davinci-002" -model = "code-cushman-001" - -# Provider-specific models through Portkey -model = "anthropic/claude-instant-1" -model = "cohere/command" -model = "huggingface/gpt2" -``` - -### Migration from Legacy Models - -```python -# Old GPT-3 usage -response = portkey.completions.create( - prompt="Translate to French: Hello, how are you?", - model="text-davinci-003", - max_tokens=60 -) - -# Recommended migration to GPT-3.5 Instruct -response = portkey.completions.create( - prompt="Translate to French: Hello, how are you?", - model="gpt-3.5-turbo-instruct", - max_tokens=60 -) - -# Or better: migrate to chat completions -response = portkey.chat.completions.create( - messages=[ - {"role": "user", "content": "Translate to French: Hello, how are you?"} - ], - model="gpt-3.5-turbo", - max_tokens=60 -) -``` - -## Common Use Cases - -### Text Generation - -```python -# Creative writing -response = portkey.completions.create( - prompt="Write a haiku about programming:", - model="gpt-3.5-turbo-instruct", - max_tokens=30, - temperature=0.8 -) - -# Technical documentation -response = portkey.completions.create( - prompt="Function documentation for bubble sort algorithm:", - model="gpt-3.5-turbo-instruct", - max_tokens=100, - temperature=0.3 -) -``` - -### Code Completion - -```python -# Python function completion -response = portkey.completions.create( - prompt="def merge_sort(arr):\n if len(arr) <= 1:\n return arr\n \n mid = len(arr) // 2\n left = arr[:mid]\n right = arr[mid:]\n \n ", - model="gpt-3.5-turbo-instruct", - max_tokens=200, - temperature=0.2, - stop=["\n\n"] -) -``` - -### Data Processing - -```python -# Text classification -response = portkey.completions.create( - prompt="Classify the sentiment of this text as positive, negative, or neutral:\n\n\"I absolutely love this new restaurant! The food was amazing.\"\n\nSentiment:", - model="gpt-3.5-turbo-instruct", - max_tokens=10, - temperature=0 -) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md deleted file mode 100644 index c868e3ee2..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/uploads.md +++ /dev/null @@ -1,376 +0,0 @@ -# Uploads - -Large file upload system with support for chunked uploads and multi-part file handling. Enables efficient upload of large files through resumable, chunked upload process with integrity verification. - -## Capabilities - -### Upload Management - -Create, manage, and complete large file uploads using multi-part upload process for improved reliability and performance. - -```python { .api } -class Uploads: - def create( - self, - *, - bytes: int, - filename: str, - mime_type: str, - purpose: Any, - **kwargs - ) -> Upload: - """ - Create a new upload session for large files. - - Args: - bytes: Total size of the file in bytes - filename: Name of the file being uploaded - mime_type: MIME type of the file (e.g., "image/jpeg", "application/pdf") - purpose: Purpose of the upload (e.g., "assistants", "fine-tune") - **kwargs: Additional upload parameters - - Returns: - Upload: Upload session object with upload ID and status - """ - - def upload_file_chunked( - self, - *, - file: Union[os.PathLike[str], bytes], - mime_type: str, - purpose: Any, - filename: Union[str, None] = None, - bytes: Union[int, None] = None, - part_size: Union[int, None] = None, - md5: Union[str, NotGiven] = NOT_GIVEN - ) -> Any: - """ - Upload a file using automatic chunking for large files. - - Args: - file: File path or file content bytes - mime_type: MIME type of the file - purpose: Purpose of the upload - filename: Optional filename override - bytes: Optional file size override - part_size: Size of each chunk (default: 8MB) - md5: MD5 hash for integrity verification - - Returns: - File object after successful upload - """ - - def complete( - self, - upload_id: str, - *, - part_ids: List[str], - md5: Union[str, NotGiven] = NOT_GIVEN, - **kwargs - ) -> Upload: - """ - Complete a multi-part upload by assembling all parts. - - Args: - upload_id: Upload session identifier - part_ids: List of part IDs in order - md5: MD5 hash of complete file for verification - **kwargs: Additional completion parameters - - Returns: - Upload: Completed upload object - """ - - def cancel(self, upload_id: str, **kwargs) -> Upload: - """ - Cancel an in-progress upload session. - - Args: - upload_id: Upload session identifier - **kwargs: Additional cancellation parameters - - Returns: - Upload: Cancelled upload object - """ - - parts: Parts - -class AsyncUploads: - async def create( - self, - *, - bytes: int, - filename: str, - mime_type: str, - purpose: Any, - **kwargs - ) -> Upload: - """Async version of create method.""" - - async def upload_file_chunked( - self, - *, - file: Union[os.PathLike[str], bytes], - mime_type: str, - purpose: Any, - filename: Union[str, None] = None, - bytes: Union[int, None] = None, - part_size: Union[int, None] = None, - md5: Union[str, NotGiven] = NOT_GIVEN - ) -> Any: - """Async version of chunked upload method.""" - - async def complete( - self, - upload_id: str, - *, - part_ids: List[str], - md5: Union[str, NotGiven] = NOT_GIVEN, - **kwargs - ) -> Upload: - """Async version of complete method.""" - - async def cancel(self, upload_id: str, **kwargs) -> Upload: - """Async version of cancel method.""" - - parts: AsyncParts -``` - -### Part Management - -Handle individual parts of multi-part uploads for fine-grained control over the upload process. - -```python { .api } -class Parts: - def create( - self, - upload_id: str, - *, - data: FileTypes, - **kwargs - ) -> UploadPart: - """ - Upload a single part of a multi-part upload. - - Args: - upload_id: Upload session identifier - data: Part data as file-like object or bytes - **kwargs: Additional part parameters - - Returns: - UploadPart: Part object with part ID and status - """ - -class AsyncParts: - async def create( - self, - upload_id: str, - *, - data: FileTypes, - **kwargs - ) -> UploadPart: - """Async version of part creation.""" -``` - -### Usage Examples - -```python -import os -from portkey_ai import Portkey - -# Initialize client -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Simple chunked upload (recommended for most use cases) -with open("large_document.pdf", "rb") as f: - uploaded_file = portkey.uploads.upload_file_chunked( - file=f, - mime_type="application/pdf", - purpose="assistants", - filename="large_document.pdf" - ) - -print(f"File uploaded successfully: {uploaded_file.id}") - -# Manual multi-part upload for advanced use cases -file_path = "very_large_file.zip" -file_size = os.path.getsize(file_path) - -# Create upload session -upload = portkey.uploads.create( - bytes=file_size, - filename="very_large_file.zip", - mime_type="application/zip", - purpose="assistants" -) - -print(f"Upload session created: {upload.id}") - -# Upload parts manually (8MB chunks) -part_size = 8 * 1024 * 1024 # 8MB -part_ids = [] - -with open(file_path, "rb") as f: - part_number = 1 - while True: - chunk = f.read(part_size) - if not chunk: - break - - part = portkey.uploads.parts.create( - upload_id=upload.id, - data=chunk - ) - part_ids.append(part.id) - print(f"Uploaded part {part_number}: {part.id}") - part_number += 1 - -# Complete the upload -completed_upload = portkey.uploads.complete( - upload_id=upload.id, - part_ids=part_ids -) - -print(f"Upload completed: {completed_upload.status}") -print(f"File ID: {completed_upload.file.id}") -``` - -### Async Usage - -```python -import asyncio -import os -from portkey_ai import AsyncPortkey - -async def upload_large_file(): - portkey = AsyncPortkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" - ) - - # Async chunked upload - with open("large_dataset.csv", "rb") as f: - uploaded_file = await portkey.uploads.upload_file_chunked( - file=f, - mime_type="text/csv", - purpose="fine-tune", - part_size=16 * 1024 * 1024 # 16MB chunks - ) - - return uploaded_file - -# Run async upload -uploaded_file = asyncio.run(upload_large_file()) -print(f"Async upload completed: {uploaded_file.id}") -``` - -### Error Handling and Resumption - -```python -import hashlib -import os - -def upload_with_verification(): - file_path = "important_document.pdf" - - # Calculate MD5 for integrity verification - with open(file_path, "rb") as f: - file_hash = hashlib.md5(f.read()).hexdigest() - - file_size = os.path.getsize(file_path) - - try: - # Create upload with MD5 - upload = portkey.uploads.create( - bytes=file_size, - filename=os.path.basename(file_path), - mime_type="application/pdf", - purpose="assistants" - ) - - # Upload with chunked method including hash - with open(file_path, "rb") as f: - uploaded_file = portkey.uploads.upload_file_chunked( - file=f, - mime_type="application/pdf", - purpose="assistants", - md5=file_hash - ) - - print(f"Upload successful with verification: {uploaded_file.id}") - return uploaded_file - - except Exception as e: - print(f"Upload failed: {e}") - # Cancel the upload session if it was created - if 'upload' in locals(): - try: - cancelled = portkey.uploads.cancel(upload.id) - print(f"Upload cancelled: {cancelled.status}") - except Exception as cancel_error: - print(f"Failed to cancel upload: {cancel_error}") - - raise - -# Example with retry logic -def upload_with_retry(file_path, max_retries=3): - for attempt in range(max_retries): - try: - return upload_with_verification() - except Exception as e: - print(f"Attempt {attempt + 1} failed: {e}") - if attempt == max_retries - 1: - raise - # Wait before retry - import time - time.sleep(2 ** attempt) -``` - -## Types - -```python { .api } -class Upload: - """Upload session object""" - id: str # Upload identifier - object: str # "upload" - bytes: int # Total file size - created_at: int # Unix timestamp - filename: str # Original filename - purpose: str # Upload purpose - status: str # Upload status ("pending", "completed", "cancelled", "failed") - expires_at: int # Session expiration timestamp - file: Optional[FileObject] # File object after completion - _headers: Optional[dict] # Response headers - -class UploadPart: - """Individual upload part""" - id: str # Part identifier - object: str # "upload.part" - created_at: int # Unix timestamp - upload_id: str # Parent upload identifier - etag: str # Part ETag for integrity - _headers: Optional[dict] # Response headers - -class FileObject: - """Completed file object""" - id: str # File identifier - object: str # "file" - bytes: int # File size - created_at: int # Unix timestamp - filename: str # File name - purpose: str # File purpose - status: str # Processing status - status_details: Optional[str] # Status details if applicable - -FileTypes = Union[ - bytes, - str, - os.PathLike[str], - typing.IO[bytes], - typing.IO[str] -] -"""Supported file input types for uploads""" -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md b/.tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md deleted file mode 100644 index 43b6a2e0a..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/docs/vector-stores.md +++ /dev/null @@ -1,58 +0,0 @@ -# Vector Stores - -Vector database operations for retrieval-augmented generation (RAG) applications with file management and batch processing. - -## Capabilities - -### Vector Store Management - -```python { .api } -class VectorStores: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def update(self, **kwargs): ... - def delete(self, **kwargs): ... - files: VectorFiles - file_batches: VectorFileBatches - -class VectorFiles: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def delete(self, **kwargs): ... - -class VectorFileBatches: - def create(self, **kwargs): ... - def list(self, **kwargs): ... - def retrieve(self, **kwargs): ... - def cancel(self, **kwargs): ... -``` - -## Usage Examples - -```python -from portkey_ai import Portkey - -portkey = Portkey( - api_key="PORTKEY_API_KEY", - virtual_key="VIRTUAL_KEY" -) - -# Create vector store -vector_store = portkey.beta.vector_stores.create( - name="Knowledge Base" -) - -# Add files to vector store -portkey.beta.vector_stores.files.create( - vector_store_id=vector_store.id, - file_id="file-123" -) - -# Create file batch -batch = portkey.beta.vector_stores.file_batches.create( - vector_store_id=vector_store.id, - file_ids=["file-123", "file-456"] -) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-portkey-ai/tile.json b/.tessl/tiles/tessl/pypi-portkey-ai/tile.json deleted file mode 100644 index 636ab7d07..000000000 --- a/.tessl/tiles/tessl/pypi-portkey-ai/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-portkey-ai", - "version": "1.14.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/portkey-ai@1.14.4", - "summary": "Python client library for the Portkey API - Control Panel for AI Apps with unified API signature, automated fallbacks, retries, load balancing, semantic caching, virtual keys, and comprehensive observability features." -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md deleted file mode 100644 index 1cb715f5c..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/cli-commands.md +++ /dev/null @@ -1,311 +0,0 @@ -# CLI Commands - -Complete command-line interface for pre-commit hook management. All commands are executed through the `pre-commit` console script entry point, which routes to the appropriate command implementations. - -## Capabilities - -### Main Entry Point - -Primary entry point for all CLI commands that parses arguments and routes to appropriate command handlers. - -```python { .api } -def main(argv: Sequence[str] | None = None) -> int: - """ - Main CLI entry point for pre-commit commands. - - Parameters: - - argv: Optional command-line arguments array - - Returns: - - int: Exit code (0 for success, non-zero for failure) - """ -``` - -### Installation Commands - -Commands for installing and managing pre-commit hook scripts in Git repositories. - -```python { .api } -def install( - config_file: str, - store: Store, - hook_types: list[str] | None, - overwrite: bool = False, - hooks: bool = False, - skip_on_missing_config: bool = False, - git_dir: str | None = None, -) -> int: - """ - Install git hook scripts. - - Parameters: - - config_file: Path to configuration file - - store: Store instance for hook management - - hook_types: List of git hook types to install - - overwrite: Whether to overwrite existing hooks - - hooks: Whether to install hook environments too - - skip_on_missing_config: Skip installation if config missing - - git_dir: Git directory path (optional) - - Returns: - - int: Exit code - """ - -def uninstall(config_file: str, hook_types: list[str] | None) -> int: - """ - Remove git hook scripts. - - Parameters: - - config_file: Path to configuration file - - hook_types: List of git hook types to uninstall - - Returns: - - int: Exit code - """ - -def install_hooks(config_file: str, store: Store) -> int: - """ - Install hook environments for all configured hooks. - - Parameters: - - config_file: Path to configuration file - - store: Store instance for hook management - - Returns: - - int: Exit code - """ - -def init_templatedir( - config_file: str, - store: Store, - directory: str, - hook_types: list[str] | None, - skip_on_missing_config: bool = True, -) -> int: - """ - Setup hooks in git template directory. - - Parameters: - - config_file: Path to configuration file - - store: Store instance for hook management - - directory: Template directory path - - hook_types: List of git hook types to install - - skip_on_missing_config: Skip installation if config missing - - Returns: - - int: Exit code - """ -``` - -### Hook Execution Commands - -Commands for running pre-commit hooks on files with various filtering and execution options. - -```python { .api } -def run( - config_file: str, - store: Store, - args: argparse.Namespace, - environ: MutableMapping[str, str] = os.environ, -) -> int: - """ - Execute hooks with file filtering and staging options. - - Parameters: - - config_file: Path to configuration file - - store: Store instance for hook management - - all_files: Run on all files instead of just staged - - files: Specific files to run on - - hook_stage: Git hook stage to run - - hook: Run only specific hook by id - - from_ref: Git ref to compare from - - to_ref: Git ref to compare to - - show_diff_on_failure: Show diff when hooks fail - - color: Color output mode ('auto', 'always', 'never') - - verbose: Verbose output - - Returns: - - int: Exit code (0 if all hooks pass) - """ - -def hook_impl(config_file: str, hook_type: str, hook_dir: str | None = None, - skip_on_missing_config: bool = False, **kwargs) -> int: - """ - Internal hook implementation (not for direct user use). - - Parameters: - - config_file: Path to configuration file - - hook_type: Type of git hook being executed - - hook_dir: Hook directory path - - skip_on_missing_config: Skip if config missing - - Returns: - - int: Exit code - """ -``` - -### Configuration Management Commands - -Commands for managing, validating, and updating pre-commit configuration files. - -```python { .api } -def autoupdate(config_file: str = '.pre-commit-config.yaml', store: Store | None = None, - tags_only: bool = False, bleeding_edge: bool = False, - freeze: bool = False, repo: Sequence[str] = (), - jobs: int = 1) -> int: - """ - Auto-update repository versions in configuration. - - Parameters: - - config_file: Path to configuration file - - store: Store instance for repository management - - tags_only: Only use tagged versions - - bleeding_edge: Use latest commit instead of latest tag - - freeze: Store exact commit hash instead of tag - - repo: Update only specific repositories - - jobs: Number of concurrent jobs - - Returns: - - int: Exit code - """ - -def migrate_config(config_file: str = '.pre-commit-config.yaml') -> int: - """ - Migrate configuration file to latest format. - - Parameters: - - config_file: Path to configuration file - - Returns: - - int: Exit code - """ - -def validate_config(filenames: Sequence[str]) -> int: - """ - Validate .pre-commit-config.yaml files. - - Parameters: - - filenames: List of config files to validate - - Returns: - - int: Exit code (0 if all valid) - """ - -def validate_manifest(filenames: Sequence[str]) -> int: - """ - Validate .pre-commit-hooks.yaml manifest files. - - Parameters: - - filenames: List of manifest files to validate - - Returns: - - int: Exit code (0 if all valid) - """ -``` - -### Utility Commands - -Utility commands for testing, configuration generation, and maintenance operations. - -```python { .api } -def sample_config() -> int: - """ - Generate sample .pre-commit-config.yaml configuration. - - Returns: - - int: Exit code - """ - -def try_repo(repo: str, ref: str = 'HEAD', **kwargs) -> int: - """ - Test hooks from a repository without modifying configuration. - - Parameters: - - repo: Repository URL or path - - ref: Git reference to use - - Returns: - - int: Exit code - """ - -def clean(store: Store | None = None) -> int: - """ - Clean pre-commit cache and temporary files. - - Parameters: - - store: Store instance for cleanup operations - - Returns: - - int: Exit code - """ - -def gc(store: Store | None = None) -> int: - """ - Garbage collect unused repositories and environments. - - Parameters: - - store: Store instance for garbage collection - - Returns: - - int: Exit code - """ -``` - -## Hook Types and Stages - -### Supported Git Hook Types - -```python { .api } -HOOK_TYPES = ( - 'commit-msg', 'post-checkout', 'post-commit', 'post-merge', - 'post-rewrite', 'pre-commit', 'pre-merge-commit', 'pre-push', - 'pre-rebase', 'prepare-commit-msg' -) - -STAGES = (*HOOK_TYPES, 'manual') -``` - -### Commands Without Git Repository Requirement - -```python { .api } -COMMANDS_NO_GIT = { - 'clean', 'gc', 'init-templatedir', 'sample-config', - 'validate-config', 'validate-manifest', -} -``` - -## Usage Examples - -### Basic Command Usage - -```python -from pre_commit.main import main - -# Install hooks -exit_code = main(['install']) - -# Run all hooks -exit_code = main(['run', '--all-files']) - -# Auto-update configuration -exit_code = main(['autoupdate']) - -# Validate configuration -exit_code = main(['validate-config', '.pre-commit-config.yaml']) -``` - -### Advanced Command Usage - -```python -# Run specific hook with verbose output -exit_code = main(['run', '--hook', 'flake8', '--verbose']) - -# Run on specific files -exit_code = main(['run', '--files', 'file1.py', 'file2.py']) - -# Show diff on failure -exit_code = main(['run', '--show-diff-on-failure']) - -# Try hooks from external repository -exit_code = main(['try-repo', 'https://github.com/psf/black']) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md deleted file mode 100644 index 3dcbbdc5f..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/configuration.md +++ /dev/null @@ -1,241 +0,0 @@ -# Configuration Management - -Configuration loading, validation, and schema definitions for pre-commit configuration files. Pre-commit uses YAML-based configuration files to define hooks and their settings. - -## Capabilities - -### Configuration Loading - -Functions for loading and parsing pre-commit configuration files with automatic validation. - -```python { .api } -load_config: functools.partial[dict[str, Any]] -# Partial function for loading .pre-commit-config.yaml files -# Usage: config = load_config(filename) -# Raises InvalidConfigError if configuration is invalid - -load_manifest: functools.partial[list[dict[str, Any]]] -# Partial function for loading .pre-commit-hooks.yaml files -# Usage: hooks = load_manifest(filename) -# Raises InvalidManifestError if manifest is invalid -``` - -### Configuration Validation Functions - -Utility functions for validating configuration elements. - -```python { .api } -def check_type_tag(tag: str) -> None: - """Validate file type tag is recognized""" - -def parse_version(s: str) -> tuple[int, ...]: - """Parse version string for comparison""" - -def check_min_version(version: str) -> None: - """Check minimum pre-commit version requirement""" - -def transform_stage(stage: str) -> str: - """Transform legacy stage names to current format""" -``` - -### Schema Constants - -JSON schemas used for validating configuration and manifest files. - -```python { .api } -CONFIG_SCHEMA: cfgv.Map -"""Schema for .pre-commit-config.yaml validation""" - -MANIFEST_SCHEMA: cfgv.Array -"""Schema for .pre-commit-hooks.yaml validation""" - -MINIMAL_MANIFEST_SCHEMA: cfgv.Array -"""Minimal schema for manifest validation""" -``` - -## Configuration File Formats - -### Pre-commit Configuration (.pre-commit-config.yaml) - -Main configuration file format for defining repositories and hooks: - -```yaml -# .pre-commit-config.yaml example -repos: - - repo: https://github.com/psf/black - rev: 22.3.0 - hooks: - - id: black - args: [--line-length=88] - files: \.py$ - - repo: https://github.com/pycqa/flake8 - rev: 4.0.1 - hooks: - - id: flake8 - additional_dependencies: [flake8-docstrings] -``` - -#### Configuration Schema Properties - -```python { .api } -class ConfigRepo: - """Configuration repository definition""" - repo: str # Repository URL or 'local' - rev: str # Git revision (tag, commit, branch) - hooks: list[ConfigHook] # List of hooks from this repo - -class ConfigHook: - """Hook configuration in .pre-commit-config.yaml""" - id: str # Hook identifier - alias: str | None = None # Alternative name - name: str | None = None # Display name override - language_version: str | None = None # Language version - files: str | None = None # File pattern regex - exclude: str | None = None # Exclusion pattern - types: list[str] | None = None # File types to include - types_or: list[str] | None = None # Alternative file types - exclude_types: list[str] | None = None # File types to exclude - args: list[str] | None = None # Additional arguments - stages: list[str] | None = None # Git hook stages - additional_dependencies: list[str] | None = None # Extra deps - always_run: bool | None = None # Run even without files - verbose: bool | None = None # Verbose output - log_file: str | None = None # Log file path -``` - -### Hook Manifest (.pre-commit-hooks.yaml) - -Manifest file format for defining available hooks in a repository: - -```yaml -# .pre-commit-hooks.yaml example -- id: black - name: black - description: "The uncompromising Python code formatter" - entry: black - language: python - require_serial: false - types_or: [python, pyi] - minimum_pre_commit_version: 2.9.2 -``` - -#### Manifest Schema Properties - -```python { .api } -class ManifestHook: - """Hook definition in .pre-commit-hooks.yaml""" - id: str # Unique identifier - name: str # Display name - entry: str # Command to execute - language: str # Programming language - description: str | None = None # Hook description - alias: str | None = None # Alternative name - files: str | None = None # File pattern regex - exclude: str | None = None # Exclusion pattern - types: list[str] | None = None # File types - types_or: list[str] | None = None # Alternative file types - exclude_types: list[str] | None = None # Excluded file types - always_run: bool = False # Always execute - fail_fast: bool = False # Stop on first failure - pass_filenames: bool = True # Pass filenames to hook - require_serial: bool = False # Serial execution required - stages: list[str] | None = None # Applicable stages - args: list[str] | None = None # Default arguments - verbose: bool = False # Verbose output - language_version: str = 'default' # Language version - log_file: str | None = None # Log file path - minimum_pre_commit_version: str = '0.15.0' # Version requirement - additional_dependencies: list[str] = [] # Extra dependencies -``` - -## File Type Detection - -### Supported File Types - -Pre-commit includes built-in file type detection for various programming languages and file formats: - -```python { .api } -FILE_TYPES = [ - 'bash', 'batch', 'c', 'c++', 'csharp', 'css', 'dart', 'dockerfile', - 'elixir', 'go', 'haskell', 'html', 'java', 'javascript', 'json', - 'jsx', 'kotlin', 'lua', 'markdown', 'perl', 'php', 'python', 'r', - 'ruby', 'rust', 'scala', 'shell', 'sql', 'swift', 'text', 'toml', - 'tsx', 'typescript', 'xml', 'yaml' -] -``` - -## Configuration Examples - -### Loading Configuration - -```python -from pre_commit.clientlib import load_config, InvalidConfigError - -try: - config = load_config('.pre-commit-config.yaml') - print(f"Found {len(config['repos'])} repositories") - - for repo in config['repos']: - print(f"Repository: {repo['repo']}") - print(f"Hooks: {[hook['id'] for hook in repo['hooks']]}") - -except InvalidConfigError as e: - print(f"Configuration error: {e}") -``` - -### Loading Manifest - -```python -from pre_commit.clientlib import load_manifest, InvalidManifestError - -try: - manifest = load_manifest('.pre-commit-hooks.yaml') - print(f"Found {len(manifest)} hook definitions") - - for hook in manifest: - print(f"Hook: {hook['id']} ({hook['language']})") - -except InvalidManifestError as e: - print(f"Manifest error: {e}") -``` - -### Configuration Validation Functions - -```python -from pre_commit.clientlib import check_type_tag, parse_version, check_min_version - -# Validate file type tags -try: - check_type_tag('python') # Valid tag - check_type_tag('invalid') # Would raise ValidationError -except cfgv.ValidationError as e: - print(f"Invalid type tag: {e}") - -# Parse and compare versions -version_tuple = parse_version('1.2.3') # Returns (1, 2, 3) - -# Check minimum version requirement -try: - check_min_version('2.0.0') -except cfgv.ValidationError as e: - print(f"Version too old: {e}") -``` - -## Constants - -### Configuration File Names - -```python { .api } -CONFIG_FILE: str = '.pre-commit-config.yaml' -"""Default configuration file name""" - -MANIFEST_FILE: str = '.pre-commit-hooks.yaml' -"""Default manifest file name""" -``` - -### Validation Settings - -```python { .api } -DEFAULT_LANGUAGE_VERSION: cfgv.Map -"""Schema for default language version configuration""" -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md deleted file mode 100644 index 0d763ecdc..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/git-integration.md +++ /dev/null @@ -1,393 +0,0 @@ -# Git Integration - -Git repository utilities and integration functions for working with staged files, repository state, and Git workflow integration. Pre-commit provides deep integration with Git operations to enable seamless hook execution within development workflows. - -## Capabilities - -### Repository Information - -Functions for retrieving basic repository information and paths. - -```python { .api } -def get_root() -> str: - """ - Get the root directory of the current Git repository. - - Returns: - - str: Absolute path to repository root - - Raises: - - CalledProcessError: If not in a Git repository - """ - -def get_git_dir() -> str: - """ - Get the .git directory path for the current repository. - - Returns: - - str: Path to .git directory - """ - -def get_git_common_dir() -> str: - """ - Get the common Git directory (for worktrees). - - Returns: - - str: Path to common Git directory - """ -``` - -### File Status and Listing - -Functions for retrieving file lists based on Git status and staging state. - -```python { .api } -def get_staged_files(cwd: str | None = None) -> list[str]: - """ - Get list of files currently staged for commit. - - Parameters: - - cwd: Working directory (defaults to repository root) - - Returns: - - list: Staged file paths relative to repository root - """ - -def get_all_files() -> list[str]: - """ - Get all tracked files in the repository. - - Returns: - - list: All tracked file paths relative to repository root - """ - -def get_changed_files(old: str, new: str) -> list[str]: - """ - Get files changed between two Git references. - - Parameters: - - old: Old Git reference (commit, branch, tag) - - new: New Git reference (commit, branch, tag) - - Returns: - - list: Changed file paths between references - """ - -def get_conflicted_files() -> set[str]: - """ - Get files currently in merge conflict state. - - Returns: - - set: File paths with unresolved conflicts - """ -``` - -### Repository State - -Functions for checking repository state and conditions. - -```python { .api } -def is_in_merge_conflict() -> bool: - """ - Check if repository is currently in a merge conflict state. - - Returns: - - bool: True if there are unresolved merge conflicts - """ - -def is_in_rebase() -> bool: - """ - Check if repository is currently in a rebase operation. - - Returns: - - bool: True if rebase is in progress - """ - -def is_in_cherry_pick() -> bool: - """ - Check if repository is currently in a cherry-pick operation. - - Returns: - - bool: True if cherry-pick is in progress - """ - -def has_unmerged_paths() -> bool: - """ - Check if repository has unmerged paths. - - Returns: - - bool: True if there are unmerged paths - """ -``` - -### Git Configuration - -Functions for reading and checking Git configuration settings. - -```python { .api } -def has_core_hookpaths_set() -> bool: - """ - Check if Git core.hooksPath configuration is set. - - Pre-commit needs to know if custom hook paths are configured - to handle installation correctly. - - Returns: - - bool: True if core.hooksPath is configured - """ - -def get_hook_paths() -> list[str]: - """ - Get configured Git hook paths. - - Returns: - - list: Hook directory paths - """ -``` - -### Commit and Reference Operations - -Functions for working with commits, branches, and references. - -```python { .api } -def get_commit_msg(commit_msg_filename: str) -> str: - """ - Read commit message from file. - - Parameters: - - commit_msg_filename: Path to commit message file - - Returns: - - str: Commit message content - """ - -def set_commit_msg(commit_msg_filename: str, new_msg: str) -> None: - """ - Write commit message to file. - - Parameters: - - commit_msg_filename: Path to commit message file - - new_msg: New commit message content - """ - -def get_staged_files_intent_to_add() -> list[str]: - """ - Get files staged with intent-to-add flag. - - Returns: - - list: File paths staged with git add --intent-to-add - """ -``` - -### Git Command Utilities - -Low-level utilities for executing Git commands and processing output. - -```python { .api } -def zsplit(s: str) -> list[str]: - """ - Split null-separated string into list. - - Used for processing Git output with null separators. - - Parameters: - - s: Null-separated string - - Returns: - - list: Split string components - """ - -def no_git_env(**kwargs) -> dict[str, str]: - """ - Create environment dictionary with Git variables removed. - - Useful for running commands without Git environment influence. - - Parameters: - - kwargs: Additional environment variables - - Returns: - - dict: Environment without Git variables - """ -``` - -### File Modification Detection - -Functions for detecting file changes and modifications. - -```python { .api } -def get_diff_files(ref1: str, ref2: str) -> list[str]: - """ - Get files different between two references. - - Parameters: - - ref1: First Git reference - - ref2: Second Git reference - - Returns: - - list: Files with differences - """ - -def intent_to_add_cleared() -> bool: - """ - Check if intent-to-add files have been cleared from index. - - Returns: - - bool: True if intent-to-add state is clear - """ -``` - -## Git Hook Integration - -### Hook Installation Paths - -Functions for determining where Git hooks should be installed. - -```python { .api } -def get_hook_script_path(hook_type: str) -> str: - """ - Get the file path where a Git hook script should be installed. - - Parameters: - - hook_type: Type of Git hook (pre-commit, pre-push, etc.) - - Returns: - - str: Path to hook script file - """ - -def hook_exists(hook_type: str) -> bool: - """ - Check if a Git hook script already exists. - - Parameters: - - hook_type: Type of Git hook to check - - Returns: - - bool: True if hook script exists - """ -``` - -### Git Object Utilities - -Functions for working with Git objects and SHA values. - -```python { .api } -def get_object_name(ref: str) -> str: - """ - Get the SHA hash for a Git reference. - - Parameters: - - ref: Git reference (branch, tag, commit) - - Returns: - - str: SHA hash of the object - """ - -def is_valid_sha(sha: str) -> bool: - """ - Check if string is a valid Git SHA hash. - - Parameters: - - sha: String to validate - - Returns: - - bool: True if valid SHA format - """ -``` - -## Usage Examples - -### Working with Staged Files - -```python -from pre_commit import git - -# Get all staged files for processing -staged_files = git.get_staged_files() -print(f"Processing {len(staged_files)} staged files:") -for file_path in staged_files: - print(f" {file_path}") - -# Check if we're in a merge conflict -if git.is_in_merge_conflict(): - print("Repository is in merge conflict state") - conflicted = git.get_conflicted_files() - print(f"Conflicted files: {conflicted}") -else: - print("Repository is in clean state") -``` - -### Repository Information - -```python -from pre_commit import git - -# Get repository root -repo_root = git.get_root() -print(f"Repository root: {repo_root}") - -# Check Git configuration -if git.has_core_hookpaths_set(): - print("Custom hook paths are configured") - paths = git.get_hook_paths() - print(f"Hook paths: {paths}") -else: - print("Using default Git hook paths") -``` - -### File Change Detection - -```python -from pre_commit import git - -# Get all tracked files -all_files = git.get_all_files() -print(f"Repository contains {len(all_files)} tracked files") - -# Get changes between commits -changed_files = git.get_changed_files('HEAD~1', 'HEAD') -print(f"Files changed in last commit: {len(changed_files)}") -for file_path in changed_files: - print(f" {file_path}") - -# Get files staged with intent-to-add -intent_files = git.get_staged_files_intent_to_add() -if intent_files: - print(f"Files staged with intent-to-add: {intent_files}") -``` - -### Environment Management - -```python -from pre_commit import git -import subprocess - -# Run command without Git environment variables -env = git.no_git_env(CUSTOM_VAR='value') -result = subprocess.run( - ['some-command'], - env=env, - capture_output=True, - text=True -) -``` - -### Repository State Checking - -```python -from pre_commit import git - -# Check various repository states -states = { - 'merge_conflict': git.is_in_merge_conflict(), - 'rebase': git.is_in_rebase(), - 'cherry_pick': git.is_in_cherry_pick(), - 'unmerged_paths': git.has_unmerged_paths() -} - -active_states = [name for name, active in states.items() if active] -if active_states: - print(f"Repository states: {', '.join(active_states)}") -else: - print("Repository is in normal state") -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md deleted file mode 100644 index 8406ac793..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/hooks.md +++ /dev/null @@ -1,381 +0,0 @@ -# Hook System - -Core data structures and management functions for pre-commit hooks. The hook system provides the foundation for defining, configuring, and executing individual hooks within the pre-commit framework. - -## Capabilities - -### Hook Data Structure - -Core data structure representing a configured hook with all its properties and execution parameters. - -```python { .api } -class Hook(NamedTuple): - """ - Immutable data structure representing a configured pre-commit hook. - - All hook properties are defined at initialization and cannot be modified - during execution, ensuring consistent behavior across hook runs. - """ - src: str # Repository source URL or 'local' - prefix: Prefix # Installation directory prefix - id: str # Unique hook identifier - name: str # Display name for output - entry: str # Command or script to execute - language: str # Programming language (python, node, etc.) - alias: str # Alternative name for hook - files: str # File pattern regex for inclusion - exclude: str # File pattern regex for exclusion - types: Sequence[str] # File types to include - types_or: Sequence[str] # Alternative file types (OR logic) - exclude_types: Sequence[str] # File types to exclude - additional_dependencies: Sequence[str] # Extra dependencies to install - args: Sequence[str] # Command-line arguments - always_run: bool # Run even if no files match - fail_fast: bool # Stop execution on first failure - pass_filenames: bool # Pass matched filenames to hook - description: str # Human-readable description - language_version: str # Specific language version - log_file: str # Output log file path - minimum_pre_commit_version: str # Required pre-commit version - require_serial: bool # Require serial execution - stages: Sequence[str] # Git hook stages when this runs - verbose: bool # Enable verbose output -``` - -### Prefix System - -Directory management system for hook installations and environments. - -```python { .api } -class Prefix(NamedTuple): - """ - Directory path management for hook installations. - - Provides utilities for constructing paths within hook installation - directories and checking for file existence. - """ - prefix_dir: str # Base installation directory - - def path(self, *parts: str) -> str: - """ - Construct path within prefix directory. - - Parameters: - - parts: Path components to join - - Returns: - - str: Complete path within prefix - """ - - def exists(self, *parts: str) -> bool: - """ - Check if path exists within prefix directory. - - Parameters: - - parts: Path components to check - - Returns: - - bool: True if path exists - """ - - def star(self, end: str) -> tuple[str, ...]: - """ - Glob pattern matching within prefix directory. - - Parameters: - - end: Pattern to match - - Returns: - - tuple: Matching file paths - """ -``` - -### Hook Management Functions - -Functions for processing and managing collections of hooks. - -```python { .api } -def all_hooks(root_config: dict[str, Any], store: Store) -> tuple[Hook, ...]: - """ - Extract all configured hooks from configuration. - - Processes the configuration file and creates Hook instances for all - defined hooks, handling repository cloning and hook resolution. - - Parameters: - - root_config: Loaded configuration dictionary - - store: Store instance for repository management - - Returns: - - tuple: All configured Hook instances - """ - -def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None: - """ - Install environments for all provided hooks. - - Sets up the necessary runtime environments for each hook, - including language-specific dependencies and tools. - - Parameters: - - hooks: Sequence of hooks to install environments for - - store: Store instance for environment management - """ -``` - -### Hook Filtering and Selection - -Functions for filtering hooks based on various criteria. - -```python { .api } -def filter_by_include_exclude( - hook: Hook, - filenames: Sequence[str], - include: str = '', - exclude: str = '' -) -> Sequence[str]: - """ - Filter filenames based on hook's include/exclude patterns. - - Parameters: - - hook: Hook with filtering configuration - - filenames: Files to filter - - include: Additional include pattern - - exclude: Additional exclude pattern - - Returns: - - Sequence: Filtered filenames - """ - -def classify_by_types( - filenames: Sequence[str], - types: Sequence[str], - types_or: Sequence[str] = (), - exclude_types: Sequence[str] = () -) -> Sequence[str]: - """ - Classify and filter files by type. - - Parameters: - - filenames: Files to classify - - types: Required file types (AND logic) - - types_or: Alternative file types (OR logic) - - exclude_types: File types to exclude - - Returns: - - Sequence: Files matching type criteria - """ -``` - -## Hook Execution - -### Hook Runner - -Core hook execution functionality with environment management and output handling. - -```python { .api } -def run_hook( - hook: Hook, - file_args: Sequence[str], - color: bool = True -) -> tuple[int, bytes, int]: - """ - Execute a single hook with provided file arguments. - - Parameters: - - hook: Hook instance to execute - - file_args: Files to pass to hook - - color: Enable colored output - - Returns: - - tuple: (return_code, stdout_bytes, duration_ms) - """ -``` - -### Hook Output Formatting - -Functions for formatting and displaying hook execution results. - -```python { .api } -def get_hook_message( - hook: Hook, - return_code: int, - file_count: int, - duration_ms: int, - color: bool = True -) -> str: - """ - Format hook execution result message. - - Parameters: - - hook: Executed hook - - return_code: Hook exit code - - file_count: Number of files processed - - duration_ms: Execution duration - - color: Use colored output - - Returns: - - str: Formatted status message - """ - -def format_hook_output( - hook: Hook, - output: bytes, - use_color: bool = True -) -> str: - """ - Format hook output for display. - - Parameters: - - hook: Hook that produced output - - output: Raw output bytes - - use_color: Enable colored formatting - - Returns: - - str: Formatted output string - """ -``` - -## Hook Creation and Processing - -### Hook Factory Functions - -Functions for creating Hook instances from configuration data. - -```python { .api } -def _hook_from_config( - hook: dict[str, Any], - prefix: Prefix, - manifest: dict[str, Any], - root_config: dict[str, Any] -) -> Hook: - """ - Create Hook instance from configuration and manifest data. - - Parameters: - - hook: Hook configuration from .pre-commit-config.yaml - - prefix: Installation prefix for hook - - manifest: Hook definition from .pre-commit-hooks.yaml - - root_config: Root configuration context - - Returns: - - Hook: Configured hook instance - """ -``` - -### Local Hook Support - -Support for locally defined hooks without external repositories. - -```python { .api } -def local_hook( - hook_dict: dict[str, Any], - prefix: Prefix -) -> Hook: - """ - Create hook from local configuration. - - Parameters: - - hook_dict: Local hook configuration - - prefix: Installation prefix - - Returns: - - Hook: Local hook instance - """ -``` - -## Hook Stages and Types - -### Supported Hook Stages - -```python { .api } -HOOK_TYPES = ( - 'commit-msg', # Commit message validation - 'post-checkout', # After checkout operations - 'post-commit', # After successful commit - 'post-merge', # After merge operations - 'post-rewrite', # After rewrite operations - 'pre-commit', # Before commit (default) - 'pre-merge-commit', # Before merge commits - 'pre-push', # Before push operations - 'pre-rebase', # Before rebase operations - 'prepare-commit-msg' # Commit message preparation -) - -STAGES = (*HOOK_TYPES, 'manual') # All types plus manual execution -``` - -## Usage Examples - -### Working with Hook Instances - -```python -from pre_commit.hook import Hook -from pre_commit.prefix import Prefix - -# Create a prefix for hook installation -prefix = Prefix('/path/to/hook/env') - -# Hook instances are typically created by all_hooks() -# but can be constructed directly for testing -hook = Hook( - src='https://github.com/psf/black', - prefix=prefix, - id='black', - name='black', - entry='black', - language='python', - alias='', - files='\.py$', - exclude='', - types=['python'], - types_or=[], - exclude_types=[], - additional_dependencies=[], - args=[], - always_run=False, - fail_fast=False, - pass_filenames=True, - description='The uncompromising Python code formatter', - language_version='default', - log_file='', - minimum_pre_commit_version='2.9.2', - require_serial=False, - stages=['pre-commit'], - verbose=False -) - -# Check hook properties -print(f"Hook: {hook.name} ({hook.language})") -print(f"Files pattern: {hook.files}") -print(f"Always run: {hook.always_run}") -``` - -### Loading All Hooks from Configuration - -```python -from pre_commit.repository import all_hooks -from pre_commit.clientlib import load_config -from pre_commit.store import Store - -# Load configuration and create store -config = load_config('.pre-commit-config.yaml') -store = Store() - -# Get all configured hooks -hooks = all_hooks(config, store) - -print(f"Found {len(hooks)} hooks:") -for hook in hooks: - print(f" {hook.id}: {hook.name} ({hook.language})") -``` - -### Installing Hook Environments - -```python -from pre_commit.repository import install_hook_envs - -# Install environments for all hooks -install_hook_envs(hooks, store) -print("All hook environments installed") -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/index.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/index.md deleted file mode 100644 index 164eee6db..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/index.md +++ /dev/null @@ -1,297 +0,0 @@ -# Pre-commit - -A framework for managing and maintaining multi-language pre-commit hooks. Pre-commit enables automatic execution of code quality checks, linting, formatting, and testing before commits are made to version control systems, supporting multiple programming languages and providing a plugin-based architecture for extensibility. - -## Package Information - -- **Package Name**: pre-commit -- **Package Type**: pypi -- **Language**: Python -- **Installation**: `pip install pre-commit` - -## Core Imports - -```python -import pre_commit -``` - -For programmatic API usage: - -```python -from pre_commit.main import main -from pre_commit.store import Store -from pre_commit.hook import Hook -from pre_commit.clientlib import load_config, load_manifest, HOOK_TYPES, STAGES -from pre_commit import git -from pre_commit import constants as C -from pre_commit.errors import FatalError, InvalidConfigError, InvalidManifestError -from pre_commit.util import cmd_output, CalledProcessError -from pre_commit.color import format_color, use_color -from pre_commit.output import write_line -``` - -## Basic Usage - -### Command Line Interface - -```bash -# Install git hook scripts -pre-commit install - -# Run hooks on all files -pre-commit run --all-files - -# Run hooks on staged files only -pre-commit run - -# Auto-update hook versions -pre-commit autoupdate - -# Validate configuration -pre-commit validate-config -``` - -### Programmatic Usage - -```python -from pre_commit.main import main -from pre_commit.store import Store -from pre_commit.clientlib import load_config, InvalidConfigError -from pre_commit.errors import FatalError - -# Run pre-commit programmatically -exit_code = main(['run', '--all-files']) - -# Load and validate configuration with error handling -try: - config = load_config('.pre-commit-config.yaml') - print(f"Loaded config with {len(config['repos'])} repositories") -except InvalidConfigError as e: - print(f"Configuration error: {e}") -except FatalError as e: - print(f"Fatal error: {e}") - -# Initialize store for hook management -store = Store() - -# Example: Clone a repository for hook usage -repo_path = store.clone('https://github.com/psf/black', 'main') -print(f"Repository cloned to: {repo_path}") -``` - -## Architecture - -Pre-commit follows a modular architecture built around several key components: - -- **CLI Commands**: Command-line interface providing installation, execution, and management functionality -- **Hook System**: Data structures and execution framework for individual hooks -- **Language Support**: Plugin-based architecture supporting 22+ programming languages -- **Configuration Management**: YAML-based configuration loading and validation -- **Git Integration**: Deep integration with Git workflows and repository operations -- **Repository Management**: Store system for managing hook repositories and environments - -This design enables pre-commit to serve as a comprehensive framework for maintaining code quality across diverse development environments and language ecosystems. - -## Capabilities - -### CLI Commands - -Complete command-line interface for installing, managing, and executing pre-commit hooks. Includes installation commands, hook execution, configuration management, and utility operations. - -```python { .api } -def main(argv: Sequence[str] | None = None) -> int -``` - -[CLI Commands](./cli-commands.md) - -### Configuration Management - -Configuration loading, validation, and schema definitions for .pre-commit-config.yaml and .pre-commit-hooks.yaml files. - -```python { .api } -load_config: functools.partial[dict[str, Any]] -# Partial function for loading .pre-commit-config.yaml files -# Usage: config = load_config(filename) - -load_manifest: functools.partial[list[dict[str, Any]]] -# Partial function for loading .pre-commit-hooks.yaml files -# Usage: hooks = load_manifest(filename) -``` - -[Configuration](./configuration.md) - -### Hook System - -Core data structures and management functions for pre-commit hooks, including hook representation, execution, and environment management. - -```python { .api } -class Hook(NamedTuple): - src: str - prefix: Prefix - id: str - name: str - entry: str - language: str - alias: str - files: str - exclude: str - types: Sequence[str] - types_or: Sequence[str] - exclude_types: Sequence[str] - additional_dependencies: Sequence[str] - args: Sequence[str] - always_run: bool - fail_fast: bool - pass_filenames: bool - description: str - language_version: str - log_file: str - minimum_pre_commit_version: str - require_serial: bool - stages: Sequence[str] - verbose: bool -``` - -[Hooks](./hooks.md) - -### Git Integration - -Git repository utilities and integration functions for working with staged files, repository state, and Git workflow integration. - -```python { .api } -def get_root() -> str -def get_staged_files(cwd: str | None = None) -> list[str] -def get_all_files() -> list[str] -``` - -[Git Integration](./git-integration.md) - -### Language Support - -Multi-language support system with plugin-based architecture for 22+ programming languages including Python, JavaScript, Go, Rust, and more. - -```python { .api } -class Language(Protocol): - def get_default_version(self) -> str - def install_environment(self, prefix: Prefix, version: str, additional_dependencies: Sequence[str]) -> None - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes] -``` - -[Language Support](./language-support.md) - -### Repository Management - -Repository and hook environment management through the Store system, including cloning, caching, and cleanup operations. - -```python { .api } -class Store: - def clone(self, repo: str, ref: str, deps: Sequence[str] = ()) -> str - def make_local(self, deps: Sequence[str]) -> str - def select_all_repos(self) -> list[tuple[str, str, str]] -``` - -[Repository Management](./repository-management.md) - -## Constants and Error Handling - -### Constants - -```python { .api } -CONFIG_FILE: str = '.pre-commit-config.yaml' -MANIFEST_FILE: str = '.pre-commit-hooks.yaml' -LOCAL_REPO_VERSION: str = '1' -DEFAULT: str = 'default' -VERSION: str # Package version string - -# Hook types and stages -HOOK_TYPES: tuple[str, ...] = ( - 'commit-msg', 'post-checkout', 'post-commit', 'post-merge', - 'post-rewrite', 'pre-commit', 'pre-merge-commit', 'pre-push', - 'pre-rebase', 'prepare-commit-msg' -) -STAGES: tuple[str, ...] = (*HOOK_TYPES, 'manual') - -# Repository types -LOCAL: str = 'local' -META: str = 'meta' - -# Color constants -RED: str -GREEN: str -YELLOW: str -TURQUOISE: str -SUBTLE: str -NORMAL: str -COLOR_CHOICES: tuple[str, ...] = ('auto', 'always', 'never') -``` - -### Exceptions - -```python { .api } -class FatalError(RuntimeError): - """Base exception for fatal errors""" - -class InvalidConfigError(FatalError): - """Configuration validation errors""" - -class InvalidManifestError(FatalError): - """Manifest validation errors""" - -class CalledProcessError(RuntimeError): - """Enhanced subprocess error with stdout/stderr capture""" - returncode: int - cmd: tuple[str, ...] - stdout: bytes - stderr: bytes | None -``` - -### Utility Functions - -Core utility functions for process execution, file operations, and resource management. - -```python { .api } -def cmd_output(*cmd: str, **kwargs: Any) -> tuple[int, str, str | None]: - """Execute command and return exit code, stdout, stderr as strings""" - -def cmd_output_b(*cmd: str, check: bool = True, **kwargs: Any) -> tuple[int, bytes, bytes | None]: - """Execute command and return exit code, stdout, stderr as bytes""" - -def make_executable(filename: str) -> None: - """Make file executable""" - -def resource_text(filename: str) -> str: - """Read text from package resource file""" - -def clean_path_on_failure(path: str) -> Generator[None, None, None]: - """Context manager that removes path on exception""" -``` - -### Color Support - -Functions for colored terminal output. - -```python { .api } -def format_color(text: str, color: str, use_color_setting: bool) -> str: - """Format text with ANSI color codes""" - -def use_color(setting: str) -> bool: - """Determine if color should be used based on setting""" - -def add_color_option(parser: argparse.ArgumentParser) -> None: - """Add --color argument to argument parser""" -``` - -### Output Functions - -Terminal output utilities with optional logging support. - -```python { .api } -def write(s: str, stream: IO[bytes] = sys.stdout.buffer) -> None: - """Write string to byte stream""" - -def write_line(s: str | None = None, **kwargs: Any) -> None: - """Write line to output with optional logging""" - -def write_line_b(s: bytes | None = None, stream: IO[bytes] = sys.stdout.buffer, logfile_name: str | None = None) -> None: - """Write line as bytes to output with optional logging""" -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md deleted file mode 100644 index 62c55c318..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/language-support.md +++ /dev/null @@ -1,494 +0,0 @@ -# Language Support - -Multi-language support system with plugin-based architecture for 22+ programming languages. Pre-commit provides a standardized interface for integrating hooks written in different programming languages, each with their own environment management and execution requirements. - -## Capabilities - -### Language Protocol - -Base protocol that all language implementations must follow. - -```python { .api } -class Language(Protocol): - """ - Protocol defining the interface for language implementations. - - Each supported language must implement this protocol to provide - consistent environment management and hook execution. - """ - - @property - def ENVIRONMENT_DIR(self) -> str | None: - """ - Directory name for language-specific environments. - - Returns None for languages that don't require separate environments. - - Returns: - - str | None: Environment directory name or None - """ - - def get_default_version(self) -> str: - """ - Get the default version identifier for this language. - - Returns: - - str: Default version string (e.g., 'default', 'system') - """ - - def health_check(self, prefix: Prefix, version: str) -> str | None: - """ - Check if the language environment is healthy and functional. - - Parameters: - - prefix: Installation prefix for the environment - - version: Language version to check - - Returns: - - str | None: Error message if unhealthy, None if healthy - """ - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - Install or update the language environment. - - Parameters: - - prefix: Installation prefix for the environment - - version: Language version to install - - additional_dependencies: Extra packages to install - """ - - def in_env(self, prefix: Prefix, version: str) -> ContextManager[None]: - """ - Context manager for executing within the language environment. - - Parameters: - - prefix: Installation prefix for the environment - - version: Language version to use - - Returns: - - ContextManager: Context for environment activation - """ - - def run_hook( - self, - prefix: Prefix, - version: str, - cmd: Sequence[str], - *, - file_args: Sequence[str], - prepend_base_dir: bool, - require_serial: bool, - color: bool - ) -> tuple[int, bytes]: - """ - Execute a hook within the language environment. - - Parameters: - - prefix: Installation prefix for the environment - - version: Language version to use - - cmd: Command and arguments to execute - - file_args: Files to pass to the hook - - prepend_base_dir: Whether to prepend base directory to paths - - require_serial: Whether serial execution is required - - color: Whether to enable colored output - - Returns: - - tuple: (exit_code, output_bytes) - """ -``` - -### Supported Languages - -Complete registry of supported languages with their implementations. - -```python { .api } -languages: dict[str, Language] = { - 'conda': conda, # Conda package manager - 'coursier': coursier, # Scala/Coursier dependency manager - 'dart': dart, # Dart programming language - 'docker': docker, # Docker containers - 'docker_image': docker_image, # Pre-built Docker images - 'dotnet': dotnet, # .NET framework - 'fail': fail, # Always-fail hook for testing - 'golang': golang, # Go programming language - 'haskell': haskell, # Haskell programming language - 'julia': julia, # Julia programming language - 'lua': lua, # Lua programming language - 'node': node, # Node.js/npm - 'perl': perl, # Perl programming language - 'pygrep': pygrep, # Python regex-based hooks - 'python': python, # Python programming language - 'r': r, # R programming language - 'ruby': ruby, # Ruby programming language - 'rust': rust, # Rust programming language - 'script': script, # Shell scripts - 'swift': swift, # Swift programming language - 'system': system # System executables -} -``` - -## Individual Language Implementations - -### Python Language Support - -```python { .api } -class PythonLanguage: - """Python language implementation with virtual environment support.""" - - ENVIRONMENT_DIR = 'py_env' - - def get_default_version(self) -> str: - """Returns 'python3' as default Python version.""" - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - Create virtual environment and install dependencies. - - Creates a Python virtual environment and installs the hook - package along with any additional dependencies. - """ - - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: - """Execute hook within Python virtual environment.""" -``` - -### Node.js Language Support - -```python { .api } -class NodeLanguage: - """Node.js language implementation with npm package management.""" - - ENVIRONMENT_DIR = 'node_env' - - def get_default_version(self) -> str: - """Returns 'node' as default Node.js version.""" - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - Install Node.js packages using npm. - - Sets up npm environment and installs hook package with dependencies. - """ - - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: - """Execute hook with Node.js runtime.""" -``` - -### Go Language Support - -```python { .api } -class GolangLanguage: - """Go language implementation with module support.""" - - ENVIRONMENT_DIR = 'go_env' - - def get_default_version(self) -> str: - """Returns 'go' as default Go version.""" - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - Install Go modules and build binaries. - - Downloads Go modules and builds executable binaries for hook execution. - """ - - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: - """Execute Go binary hook.""" -``` - -### Ruby Language Support - -```python { .api } -class RubyLanguage: - """Ruby language implementation with gem management.""" - - ENVIRONMENT_DIR = 'ruby_env' - - def get_default_version(self) -> str: - """Returns 'ruby' as default Ruby version.""" - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - Install Ruby gems in isolated environment. - - Creates gem environment and installs hook gem with dependencies. - """ - - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: - """Execute hook with Ruby runtime.""" -``` - -### Docker Language Support - -```python { .api } -class DockerLanguage: - """Docker language implementation for containerized hooks.""" - - ENVIRONMENT_DIR = None # No local environment needed - - def get_default_version(self) -> str: - """Returns 'default' for Docker version.""" - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - Build Docker image for hook execution. - - Builds Docker image with hook and its dependencies. - """ - - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: - """Execute hook within Docker container.""" -``` - -### System Language Support - -```python { .api } -class SystemLanguage: - """System language for executing system-installed binaries.""" - - ENVIRONMENT_DIR = None # Uses system executables - - def get_default_version(self) -> str: - """Returns 'system' for system executables.""" - - def install_environment( - self, - prefix: Prefix, - version: str, - additional_dependencies: Sequence[str] - ) -> None: - """ - No installation needed for system executables. - - Validates that required executables are available on system PATH. - """ - - def run_hook(self, prefix: Prefix, version: str, cmd: Sequence[str], **kwargs) -> tuple[int, bytes]: - """Execute system executable directly.""" -``` - -## Language Utilities - -### Language Resolution - -Functions for resolving and working with language implementations. - -```python { .api } -def get_language(language: str) -> Language: - """ - Get language implementation by name. - - Parameters: - - language: Language name - - Returns: - - Language: Language implementation instance - - Raises: - - ValueError: If language is not supported - """ - -def language_version_info(language: str, version: str) -> dict[str, Any]: - """ - Get version information for a language. - - Parameters: - - language: Language name - - version: Version string - - Returns: - - dict: Version information and capabilities - """ -``` - -### Environment Management - -Utilities for managing language environments across hooks. - -```python { .api } -def clean_environment(prefix: Prefix, language: str) -> None: - """ - Clean up language environment files. - - Parameters: - - prefix: Installation prefix - - language: Language name - """ - -def environment_exists(prefix: Prefix, language: str, version: str) -> bool: - """ - Check if language environment is installed. - - Parameters: - - prefix: Installation prefix - - language: Language name - - version: Language version - - Returns: - - bool: True if environment exists - """ -``` - -## Language-Specific Features - -### Python Features - -```python { .api } -def python_venv_info(prefix: Prefix) -> dict[str, str]: - """Get Python virtual environment information.""" - -def install_python_requirements(prefix: Prefix, requirements: list[str]) -> None: - """Install Python packages from requirements list.""" -``` - -### Node.js Features - -```python { .api } -def node_package_info(prefix: Prefix) -> dict[str, Any]: - """Get Node.js package information.""" - -def install_node_packages(prefix: Prefix, packages: list[str]) -> None: - """Install Node.js packages via npm.""" -``` - -### Docker Features - -```python { .api } -def docker_image_exists(image: str) -> bool: - """Check if Docker image exists locally.""" - -def pull_docker_image(image: str) -> None: - """Pull Docker image from registry.""" -``` - -## Usage Examples - -### Working with Language Implementations - -```python -from pre_commit.languages import languages -from pre_commit.prefix import Prefix - -# Get Python language implementation -python_lang = languages['python'] -print(f"Python environment dir: {python_lang.ENVIRONMENT_DIR}") -print(f"Default version: {python_lang.get_default_version()}") - -# Create prefix for hook installation -prefix = Prefix('/tmp/hook-env') - -# Install Python environment with additional dependencies -python_lang.install_environment( - prefix=prefix, - version='python3.9', - additional_dependencies=['black', 'flake8'] -) - -# Check environment health -health_result = python_lang.health_check(prefix, 'python3.9') -if health_result is None: - print("Python environment is healthy") -else: - print(f"Environment issue: {health_result}") -``` - -### Executing Hooks with Language Support - -```python -from pre_commit.languages import languages - -# Get language implementation -node_lang = languages['node'] - -# Execute hook within Node.js environment -with node_lang.in_env(prefix, 'node'): - exit_code, output = node_lang.run_hook( - prefix=prefix, - version='node', - cmd=['eslint', '--fix'], - file_args=['src/app.js', 'src/utils.js'], - prepend_base_dir=True, - require_serial=False, - color=True - ) - -print(f"Hook exit code: {exit_code}") -if output: - print(f"Hook output: {output.decode()}") -``` - -### Language Environment Management - -```python -from pre_commit.languages import get_language, environment_exists - -# Work with Go language -go_lang = get_language('golang') - -# Check if environment exists -if environment_exists(prefix, 'golang', 'go1.19'): - print("Go environment already installed") -else: - print("Installing Go environment...") - go_lang.install_environment(prefix, 'go1.19', []) - -# Get language version info -version_info = language_version_info('golang', 'go1.19') -print(f"Go version info: {version_info}") -``` - -### Multi-Language Hook Processing - -```python -from pre_commit.languages import languages - -# Process hooks for different languages -hook_configs = [ - {'language': 'python', 'version': 'python3.9', 'deps': ['black']}, - {'language': 'node', 'version': 'node16', 'deps': ['eslint']}, - {'language': 'golang', 'version': 'go1.19', 'deps': []}, -] - -for config in hook_configs: - lang_impl = languages[config['language']] - - # Install environment - lang_impl.install_environment( - prefix=prefix, - version=config['version'], - additional_dependencies=config['deps'] - ) - - print(f"Installed {config['language']} environment") -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md b/.tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md deleted file mode 100644 index f4944d82d..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/docs/repository-management.md +++ /dev/null @@ -1,425 +0,0 @@ -# Repository Management - -Repository and hook environment management through the Store system. The Store provides centralized management of hook repositories, environment caching, and cleanup operations for efficient hook execution and storage. - -## Capabilities - -### Store Class - -Central repository and environment management system that handles cloning, caching, and lifecycle management of hook repositories. - -```python { .api } -class Store: - """ - Centralized store for managing hook repositories and environments. - - Provides caching, cloning, and cleanup functionality for hook repositories - and their associated environments. Manages the complete lifecycle from - initial repository cloning through environment installation to cleanup. - """ - - def __init__(self, directory: str | None = None) -> None: - """ - Initialize store with optional custom directory. - - Parameters: - - directory: Custom store directory path (uses default if None) - """ - - def clone(self, repo: str, ref: str, deps: Sequence[str] = ()) -> str: - """ - Clone repository and return installation path. - - Clones the specified repository at the given reference and prepares - it for hook installation. Handles caching to avoid duplicate clones. - - Parameters: - - repo: Repository URL or local path - - ref: Git reference (tag, commit, branch) - - deps: Additional dependencies for environment - - Returns: - - str: Path to cloned repository installation - """ - - def make_local(self, deps: Sequence[str]) -> str: - """ - Create local repository installation for local hooks. - - Sets up environment for locally defined hooks without external repository. - - Parameters: - - deps: Dependencies for local hook environment - - Returns: - - str: Path to local installation - """ - - def mark_config_used(self, path: str) -> None: - """ - Mark configuration as recently used for garbage collection. - - Parameters: - - path: Configuration file path to mark as used - """ - - def select_all_configs(self) -> list[str]: - """ - Get all configuration files tracked by the store. - - Returns: - - list: Paths to all tracked configuration files - """ - - def delete_configs(self, configs: list[str]) -> None: - """ - Delete specified configuration files from tracking. - - Parameters: - - configs: List of configuration paths to delete - """ - - def select_all_repos(self) -> list[tuple[str, str, str]]: - """ - Get all repositories managed by the store. - - Returns: - - list: Tuples of (repo_name, ref, path) for each repository - """ - - def delete_repo(self, db_repo_name: str, ref: str, path: str) -> None: - """ - Delete repository from store and filesystem. - - Parameters: - - db_repo_name: Database repository name - - ref: Git reference - - path: Repository path to delete - """ - - @staticmethod - def get_default_directory() -> str: - """ - Get the default store directory path. - - Returns: - - str: Default store directory (typically ~/.cache/pre-commit) - """ -``` - -### Repository Operations - -High-level functions for working with repositories through the store system. - -```python { .api } -def all_hooks(root_config: dict[str, Any], store: Store) -> tuple[Hook, ...]: - """ - Extract all configured hooks from configuration using store. - - Processes the complete configuration file, clones required repositories - through the store, and creates Hook instances for all defined hooks. - - Parameters: - - root_config: Loaded configuration dictionary - - store: Store instance for repository management - - Returns: - - tuple: All configured Hook instances - """ - -def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None: - """ - Install environments for all provided hooks using store. - - Coordinates with the store to ensure all hook environments are properly - installed and configured with their dependencies. - - Parameters: - - hooks: Sequence of hooks requiring environment installation - - store: Store instance for environment management - """ -``` - -### Store Database Management - -Internal database operations for tracking repositories and configurations. - -```python { .api } -def _get_store_db(store_dir: str) -> sqlite3.Connection: - """ - Get database connection for store operations. - - Parameters: - - store_dir: Store directory path - - Returns: - - Connection: SQLite database connection - """ - -def _init_store_db(db: sqlite3.Connection) -> None: - """ - Initialize store database with required tables. - - Parameters: - - db: Database connection to initialize - """ -``` - -## Store Configuration - -### Directory Structure - -The store maintains a structured directory layout for efficient organization: - -``` -~/.cache/pre-commit/ # Default store directory -├── db.db # SQLite database for tracking -├── repos/ # Cloned repositories -│ ├── / # Individual repository directories -│ │ ├── .git/ # Git repository data -│ │ └── # Hook implementation files -│ └── ... -└── / # Language-specific environments - ├── py_env-/ # Python virtual environments - ├── node_env-/ # Node.js environments - └── ... -``` - -### Store Initialization - -```python { .api } -def initialize_store(directory: str | None = None) -> Store: - """ - Initialize a new store instance. - - Parameters: - - directory: Custom directory (uses default if None) - - Returns: - - Store: Initialized store instance - """ -``` - -## Repository Caching - -### Cache Management - -Functions for managing repository and environment caches. - -```python { .api } -def clean_unused_repos(store: Store, configs_in_use: set[str]) -> None: - """ - Clean repositories not referenced by active configurations. - - Parameters: - - store: Store instance to clean - - configs_in_use: Set of configuration files still in use - """ - -def get_cache_stats(store: Store) -> dict[str, Any]: - """ - Get statistics about store cache usage. - - Parameters: - - store: Store instance to analyze - - Returns: - - dict: Cache statistics including size, file counts, etc. - """ -``` - -### Garbage Collection - -```python { .api } -def garbage_collect_repos(store: Store) -> int: - """ - Perform garbage collection on unused repositories. - - Removes repositories and environments that are no longer referenced - by any active configurations. - - Parameters: - - store: Store instance to garbage collect - - Returns: - - int: Number of items removed - """ -``` - -## Environment Management - -### Environment Installation - -```python { .api } -def ensure_environment( - hook: Hook, - store: Store, - language_version: str -) -> None: - """ - Ensure hook environment is installed and ready. - - Parameters: - - hook: Hook requiring environment - - store: Store instance for management - - language_version: Specific language version to use - """ -``` - -### Environment Health Checks - -```python { .api } -def check_environment_health( - prefix: Prefix, - language: str, - version: str -) -> str | None: - """ - Check if environment is healthy and functional. - - Parameters: - - prefix: Environment installation prefix - - language: Programming language - - version: Language version - - Returns: - - str | None: Error message if unhealthy, None if healthy - """ -``` - -## Usage Examples - -### Basic Store Operations - -```python -from pre_commit.store import Store -from pre_commit.clientlib import load_config -from pre_commit.repository import all_hooks, install_hook_envs - -# Initialize store (uses default directory) -store = Store() - -# Or initialize with custom directory -custom_store = Store('/path/to/custom/store') - -# Load configuration and get all hooks -config = load_config('.pre-commit-config.yaml') -hooks = all_hooks(config, store) - -print(f"Found {len(hooks)} hooks from {len(config['repos'])} repositories") - -# Install environments for all hooks -install_hook_envs(hooks, store) -print("All hook environments installed") -``` - -### Repository Cloning and Management - -```python -from pre_commit.store import Store - -store = Store() - -# Clone specific repository -repo_path = store.clone( - repo='https://github.com/psf/black', - ref='22.3.0', - deps=['black[jupyter]'] -) -print(f"Repository cloned to: {repo_path}") - -# Create local environment -local_path = store.make_local(deps=['flake8', 'mypy']) -print(f"Local environment created at: {local_path}") - -# Mark configuration as used (for garbage collection) -store.mark_config_used('.pre-commit-config.yaml') -``` - -### Store Maintenance - -```python -from pre_commit.store import Store - -store = Store() - -# Get all repositories managed by store -repos = store.select_all_repos() -print(f"Store manages {len(repos)} repositories:") -for repo_name, ref, path in repos: - print(f" {repo_name}@{ref} -> {path}") - -# Get all tracked configurations -configs = store.select_all_configs() -print(f"Tracking {len(configs)} configurations:") -for config_path in configs: - print(f" {config_path}") - -# Clean up unused configurations -# (In practice, you'd determine which are actually unused) -# store.delete_configs(['/path/to/unused/config.yaml']) -``` - -### Cache Statistics and Cleanup - -```python -from pre_commit.store import Store, get_cache_stats, garbage_collect_repos - -store = Store() - -# Get cache statistics -stats = get_cache_stats(store) -print(f"Cache statistics:") -print(f" Total repositories: {stats.get('repo_count', 0)}") -print(f" Total size: {stats.get('total_size', 0)} bytes") -print(f" Environments: {stats.get('env_count', 0)}") - -# Perform garbage collection -removed_count = garbage_collect_repos(store) -print(f"Garbage collection removed {removed_count} unused items") -``` - -### Working with Store Directory - -```python -from pre_commit.store import Store - -# Get default store directory -default_dir = Store.get_default_directory() -print(f"Default store directory: {default_dir}") - -# Initialize store with explicit directory -store = Store(directory='/tmp/pre-commit-store') - -# Verify store is using correct directory -# (Store doesn't expose directory directly, but you can check filesystem) -import os -if os.path.exists('/tmp/pre-commit-store'): - print("Custom store directory created successfully") -``` - -### Advanced Repository Operations - -```python -from pre_commit.store import Store - -store = Store() - -# Work with multiple repositories -repositories = [ - ('https://github.com/psf/black', '22.3.0'), - ('https://github.com/pycqa/flake8', '4.0.1'), - ('https://github.com/pre-commit/mirrors-mypy', 'v0.950') -] - -repo_paths = [] -for repo_url, ref in repositories: - path = store.clone(repo=repo_url, ref=ref) - repo_paths.append(path) - print(f"Cloned {repo_url}@{ref} to {path}") - -print(f"Successfully cloned {len(repo_paths)} repositories") - -# Clean up specific repository if needed -# store.delete_repo('repo-name', 'ref', '/path/to/repo') -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pre-commit/tile.json b/.tessl/tiles/tessl/pypi-pre-commit/tile.json deleted file mode 100644 index e4f228093..000000000 --- a/.tessl/tiles/tessl/pypi-pre-commit/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-pre-commit", - "version": "4.3.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/pre-commit@4.3.0", - "summary": "A framework for managing and maintaining multi-language pre-commit hooks." -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/assertions.md b/.tessl/tiles/tessl/pypi-pytest/docs/assertions.md deleted file mode 100644 index 77bf3546a..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/assertions.md +++ /dev/null @@ -1,280 +0,0 @@ -# Assertions and Exception Handling - -Context managers and utilities for asserting exceptions, warnings, and performing approximate numerical comparisons. These tools provide comprehensive testing capabilities for exception handling, warning validation, and floating-point comparisons. - -## Capabilities - -### Exception Assertion - -Context manager for asserting that specific exceptions are raised during code execution. - -```python { .api } -def raises(expected_exception, *, match=None, check=None): - """ - Context manager for asserting exceptions are raised. - - Parameters: - - expected_exception: Exception class or tuple of exception classes - - match: Regex pattern that exception message should match - - check: Callable to perform additional validation on exception - - Returns: - RaisesExc context manager or ExceptionInfo if used as function decorator - """ - -class RaisesExc: - """Context manager for exception assertion with type checking.""" - - def matches(self, expected_exception) -> bool: - """Check if captured exception matches expected type.""" - - def __enter__(self) -> ExceptionInfo: - """Enter context manager.""" - - def __exit__(self, exc_type, exc_val, exc_tb) -> bool: - """Exit context manager and validate exception.""" -``` - -**Usage Example:** - -```python -import pytest -import re - -# Basic exception assertion -def test_division_by_zero(): - with pytest.raises(ZeroDivisionError): - 1 / 0 - -# With message matching -def test_invalid_input(): - with pytest.raises(ValueError, match=r"invalid.*input"): - process_input("invalid data") - -# Multiple exception types -def test_file_operations(): - with pytest.raises((FileNotFoundError, PermissionError)): - open("/nonexistent/file", "r") - -# Custom validation -def test_custom_exception(): - with pytest.raises(CustomError) as exc_info: - raise_custom_error() - - assert exc_info.value.error_code == 123 - assert "custom message" in str(exc_info.value) -``` - -### Exception Group Assertion - -Context manager for asserting ExceptionGroup exceptions (Python 3.11+). - -```python { .api } -class RaisesGroup: - """Context manager for ExceptionGroup assertion.""" - - def matches(self, expected_exceptions) -> bool: - """Check if exception group contains expected exceptions.""" - - def expected_type(self) -> type: - """Get the expected exception group type.""" -``` - -### Exception Information - -Provides detailed information about caught exceptions with advanced introspection capabilities. - -```python { .api } -class ExceptionInfo: - """Information about caught exceptions.""" - - type: type # Exception type - value: Exception # Exception instance - tb: types.TracebackType # Traceback object - typename: str # Exception type name as string - traceback: Traceback # pytest's enhanced traceback - - def exconly(self, tryshort: bool = False) -> str: - """Return exception as string without traceback.""" - - def errisinstance(self, exc: type | tuple[type, ...]) -> bool: - """Check if exception is instance of given type(s).""" - - def match(self, regexp: str | re.Pattern) -> bool: - """Check if exception message matches regex pattern.""" - - def group_contains(self, expected_exception, *, match=None, depth: int = 1) -> bool: - """Check if exception group contains specific exception type.""" -``` - -### Warning Assertion - -Context manager for asserting that specific warnings are issued during code execution. - -```python { .api } -def warns(expected_warning, *, match=None): - """ - Context manager for asserting warnings are issued. - - Parameters: - - expected_warning: Warning class or tuple of warning classes - - match: Regex pattern that warning message should match - - Returns: - WarningsRecorder context manager - """ - -class WarningsRecorder: - """Records warnings during test execution.""" - - def pop(self, cls=Warning): - """Remove and return last warning of given class.""" - - def clear(self): - """Clear all recorded warnings.""" - - @property - def list(self) -> list: - """List of all recorded warnings.""" -``` - -**Usage Example:** - -```python -import pytest -import warnings - -def test_deprecation_warning(): - with pytest.warns(DeprecationWarning): - warnings.warn("This is deprecated", DeprecationWarning) - -def test_warning_message(): - with pytest.warns(UserWarning, match=r"custom.*warning"): - warnings.warn("This is a custom warning", UserWarning) - -def test_multiple_warnings(): - with pytest.warns() as record: - warnings.warn("Warning 1", UserWarning) - warnings.warn("Warning 2", FutureWarning) - - assert len(record) == 2 - assert record[0].category == UserWarning - assert record[1].category == FutureWarning -``` - -### Deprecation Call Assertion - -Context manager for asserting that deprecation warnings are issued during function calls. - -```python { .api } -def deprecated_call(func=None, *args, **kwargs): - """ - Context manager for asserting deprecation warnings. - - Parameters: - - func: Function to call (optional, can be used as context manager) - - args: Arguments to pass to function - - kwargs: Keyword arguments to pass to function - - Returns: - WarningsRecorder if used as context manager, function result if used as function call - """ -``` - -**Usage Example:** - -```python -import pytest - -# As context manager -def test_deprecated_usage(): - with pytest.deprecated_call(): - deprecated_function() - -# As function call -def test_deprecated_function(): - result = pytest.deprecated_call(deprecated_function, arg1, arg2) - assert result == expected_value -``` - -### Approximate Comparisons - -Create approximate comparison objects for floating-point numbers, sequences, and mappings. - -```python { .api } -def approx(expected, rel=None, abs=None, nan_ok: bool = False): - """ - Create approximate comparison objects for floating-point numbers. - - Parameters: - - expected: Expected value (number, sequence, or mapping) - - rel: Relative tolerance (default: 1e-6) - - abs: Absolute tolerance (default: 1e-12) - - nan_ok: Whether NaN values should be considered equal - - Returns: - ApproxBase object (ApproxScalar, ApproxSequence, ApproxMapping, or ApproxNumpy) - """ -``` - -**Usage Example:** - -```python -import pytest -import math - -def test_float_comparison(): - assert 0.1 + 0.2 == pytest.approx(0.3) - assert math.pi == pytest.approx(3.14159, rel=1e-4) - -def test_sequence_comparison(): - assert [0.1 + 0.2, 0.2 + 0.3] == pytest.approx([0.3, 0.5]) - -def test_dict_comparison(): - result = {"a": 0.1 + 0.2, "b": 0.2 + 0.3} - expected = {"a": 0.3, "b": 0.5} - assert result == pytest.approx(expected) - -def test_custom_tolerance(): - assert 1.0001 == pytest.approx(1.0, abs=1e-3) - assert 1.01 == pytest.approx(1.0, rel=0.02) - -def test_nan_handling(): - assert float('nan') == pytest.approx(float('nan'), nan_ok=True) -``` - -## Types - -```python { .api } -from typing import Any, Callable, Pattern, Union -import re -import types - -# Type definitions for exception handling -ExceptionClass = type[Exception] -ExceptionTuple = tuple[ExceptionClass, ...] -ExceptionSpec = Union[ExceptionClass, ExceptionTuple] - -MatchPattern = Union[str, Pattern[str]] -CheckFunction = Callable[[Exception], bool] - -class ApproxBase: - """Base class for approximate comparison objects.""" - pass - -class ApproxScalar(ApproxBase): - """Approximate comparison for scalar values.""" - pass - -class ApproxSequence(ApproxBase): - """Approximate comparison for sequences.""" - pass - -class ApproxMapping(ApproxBase): - """Approximate comparison for mappings.""" - pass - -class ApproxNumpy(ApproxBase): - """Approximate comparison for numpy arrays.""" - pass -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/configuration.md b/.tessl/tiles/tessl/pypi-pytest/docs/configuration.md deleted file mode 100644 index de827a53b..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/configuration.md +++ /dev/null @@ -1,460 +0,0 @@ -# Configuration and Plugin System - -Central configuration management and plugin architecture enabling extensive customization through hooks, configuration files, command-line options, and the pluggy-based plugin system. - -## Capabilities - -### Configuration Management - -Central configuration object providing access to all pytest settings and options. - -```python { .api } -class Config: - """Central configuration object with access to pluginmanager and hooks.""" - - # Core attributes - pluginmanager: PytestPluginManager # Plugin manager instance - invocation_params: InvocationParams # Command-line invocation parameters - option: argparse.Namespace # Parsed command-line options - rootpath: Path # Root directory path - inipath: Path | None # Path to configuration file - - def getini(self, name: str) -> Any: - """ - Get configuration value from ini file. - - Parameters: - - name: Configuration option name - - Returns: - Configuration value - """ - - def getoption(self, name: str, default=None, skip: bool = False) -> Any: - """ - Get command-line option value. - - Parameters: - - name: Option name - - default: Default value if option not set - - skip: Whether to skip if option not found - - Returns: - Option value - """ - - def addinivalue_line(self, name: str, line: str) -> None: - """ - Add line to multi-line ini configuration value. - - Parameters: - - name: Configuration option name - - line: Line to add - """ - - def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: - """Issue warning during configuration time.""" -``` - -### Plugin Management - -Plugin manager extending pluggy with pytest-specific functionality for loading and managing plugins. - -```python { .api } -class PytestPluginManager: - """Extends pluggy.PluginManager with pytest-specific functionality.""" - - def register(self, plugin, name: str | None = None) -> None: - """ - Register a plugin instance. - - Parameters: - - plugin: Plugin instance - - name: Optional plugin name - """ - - def unregister(self, plugin=None, name: str | None = None) -> Any: - """ - Unregister a plugin instance. - - Parameters: - - plugin: Plugin instance to unregister - - name: Plugin name to unregister - - Returns: - Unregistered plugin - """ - - def get_plugin(self, name: str): - """Get plugin by name.""" - - def is_registered(self, plugin) -> bool: - """Check if plugin is registered.""" - - def list_plugin_distinfo(self) -> list[tuple[Any, DistInfo]]: - """List all plugin distribution info.""" - - def list_name_plugin(self) -> list[tuple[str, Any]]: - """List all plugins with their names.""" - - def get_canonical_name(self, plugin) -> str: - """Get canonical name for plugin.""" - - def load_setuptools_entrypoints(self, group: str, names: str | None = None) -> int: - """Load plugins from setuptools entry points.""" - - def consider_env(self, name: str, value: str | None = None) -> None: - """Consider environment variable for plugin loading.""" - - def consider_preparse(self, args: list[str], exclude_only: bool = False) -> None: - """Consider command-line arguments for plugin loading.""" - - def consider_conftest(self, conftestmodule) -> None: - """Register hooks from conftest module.""" -``` - -### Hook System - -Decorators for marking functions as hook implementations and specifications. - -```python { .api } -def hookimpl(**kwargs): - """ - Mark function as hook implementation. - - Parameters: - - tryfirst: Execute this hook first - - trylast: Execute this hook last - - hookwrapper: Hook wrapper (can yield) - - optionalhook: Hook is optional - - specname: Name of hook specification - - Returns: - HookimplMarker decorator - """ - -def hookspec(**kwargs): - """ - Mark function as hook specification. - - Parameters: - - firstresult: Return first non-None result - - historic: Historic hook (replay for late registrations) - - warn_on_impl: Issue warnings for implementations - - Returns: - HookspecMarker decorator - """ -``` - -**Usage Example:** - -```python -import pytest - -# Plugin implementation -class MyPlugin: - @pytest.hookimpl(tryfirst=True) - def pytest_configure(self, config): - print("Configuring my plugin") - - @pytest.hookimpl - def pytest_collection_modifyitems(self, config, items): - # Modify collected test items - pass - - @pytest.hookimpl(hookwrapper=True) - def pytest_runtest_setup(self, item): - print(f"Setting up {item.name}") - outcome = yield - print(f"Setup complete for {item.name}") - -# Register plugin -def pytest_configure(config): - config.pluginmanager.register(MyPlugin(), "myplugin") -``` - -### Argument Parsing - -Command-line argument parser for pytest with support for options and ini values. - -```python { .api } -class Parser: - """Command-line argument parser for pytest.""" - - def addoption( - self, - *names, - action=None, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=None, - help=None, - metavar=None, - dest=None, - **kwargs - ) -> None: - """Add command-line option.""" - - def getgroup(self, name: str, description: str = "") -> OptionGroup: - """Get or create option group.""" - - def addini( - self, - name: str, - help: str, - type: str | None = None, - default=None, - **kwargs - ) -> None: - """Add ini file configuration option.""" - -class OptionGroup: - """Groups related command-line options.""" - - def addoption(self, *names, **kwargs) -> None: - """Add option to this group.""" - - def _addoption(self, *names, **kwargs) -> None: - """Internal method to add option.""" -``` - -**Usage Example:** - -```python -# In conftest.py or plugin -def pytest_addoption(parser): - parser.addoption( - "--myopt", - action="store", - default="default", - help="My custom option" - ) - - group = parser.getgroup("mygroup", "My custom options") - group.addoption( - "--verbose-mode", - action="store_true", - help="Enable verbose mode" - ) - - parser.addini( - "my_setting", - help="My configuration setting", - type="string", - default="default_value" - ) - -def pytest_configure(config): - my_opt = config.getoption("myopt") - my_setting = config.getini("my_setting") -``` - -### Exit Codes - -Enumeration of pytest exit codes for different execution outcomes. - -```python { .api } -from enum import IntEnum - -class ExitCode(IntEnum): - """Integer enum for pytest exit codes.""" - - OK = 0 # All tests passed - TESTS_FAILED = 1 # One or more tests failed - INTERRUPTED = 2 # Test run interrupted - INTERNAL_ERROR = 3 # Internal pytest error - USAGE_ERROR = 4 # Command-line usage error - NO_TESTS_RAN = 5 # No tests found/collected -``` - -### Configuration Exceptions - -```python { .api } -class UsageError(Exception): - """ - Raised for command-line usage errors. - - This exception is raised when: - - Invalid command-line arguments are provided - - Required options are missing - - Option values are invalid - """ -``` - -## Entry Points - -Main entry points for running pytest programmatically. - -```python { .api } -def main(args=None, plugins=None) -> int: - """ - Main entry point for running pytest programmatically. - - Parameters: - - args: Command-line arguments (defaults to sys.argv) - - plugins: List of plugin objects to register - - Returns: - Exit code (ExitCode enum value) - """ - -def console_main() -> int: - """ - Console script entry point. - Called by pytest command-line script. - - Returns: - Exit code - """ - -# Module-level entry point -import pytest.config.cmdline - -def cmdline.main() -> int: - """Command line interface entry point.""" -``` - -## Configuration Files - -pytest supports multiple configuration file formats: - -```python -# pytest.ini -[tool:pytest] -addopts = -v --tb=short -testpaths = tests -python_files = test_*.py *_test.py -python_classes = Test* -python_functions = test_* -markers = - slow: marks tests as slow - integration: marks tests as integration tests - -# pyproject.toml -[tool.pytest.ini_options] -addopts = "-v --tb=short" -testpaths = ["tests"] -python_files = ["test_*.py", "*_test.py"] - -# setup.cfg -[tool:pytest] -addopts = -v --tb=short -testpaths = tests -``` - -## Hook Specifications - -Comprehensive hook system for extending pytest functionality at every stage: - -```python { .api } -# Configuration and startup hooks -def pytest_configure(config: Config) -> None: - """Called after command line options are parsed and plugins loaded.""" - -def pytest_unconfigure(config: Config) -> None: - """Called before test process is exited.""" - -def pytest_sessionstart(session: Session) -> None: - """Called after Session object has been created.""" - -def pytest_sessionfinish(session: Session, exitstatus: int) -> None: - """Called after whole test run finished.""" - -# Collection hooks -def pytest_collect_file(parent: Collector, path: Path) -> Collector | None: - """Create a collector for the given path.""" - -def pytest_collection_modifyitems(config: Config, items: list[Item]) -> None: - """Called after collection is completed to modify or re-order items.""" - -def pytest_generate_tests(metafunc: Metafunc) -> None: - """Generate (parametrize) tests for the given function.""" - -# Test execution hooks -def pytest_runtest_setup(item: Item) -> None: - """Called to perform the setup phase for a test item.""" - -def pytest_runtest_call(item: Item) -> None: - """Called to run the test for test item.""" - -def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: - """Called to perform the teardown phase for a test item.""" - -def pytest_runtest_makereport(item: Item, call: CallInfo) -> TestReport | None: - """Called to create a TestReport for each phase.""" - -# Fixture hooks -def pytest_fixture_setup(fixturedef: FixtureDef, request: FixtureRequest) -> object: - """Perform fixture setup execution.""" - -def pytest_fixture_post_finalizer(fixturedef: FixtureDef, request: FixtureRequest) -> None: - """Called after fixture teardown.""" - -# Reporting hooks -def pytest_report_teststatus(report: TestReport, config: Config) -> tuple[str, str, str] | None: - """Return result-category, shortletter and verbose word for reporting.""" - -def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus: int, config: Config) -> None: - """Add sections to the terminal summary reporting.""" - -# Warning and error hooks -def pytest_warning_recorded(warning_message, when: str, nodeid: str, location: tuple[str, int, str]) -> None: - """Process a warning captured by the internal pytest warnings plugin.""" - -def pytest_internalerror(excrepr: ExceptionRepr, excinfo: ExceptionInfo) -> bool: - """Called for internal errors.""" -``` - -## Plugin Development - -```python -# conftest.py - Automatic plugin loading -def pytest_configure(config): - """Called after command line options are parsed.""" - -def pytest_collection_modifyitems(config, items): - """Called after collection is completed.""" - -def pytest_runtest_setup(item): - """Called before each test runs.""" - -# External plugin package -from setuptools import setup - -setup( - name="pytest-myplugin", - entry_points={ - "pytest11": [ - "myplugin = myplugin.plugin", - ], - }, -) -``` - -## Types - -```python { .api } -from typing import Any -from pathlib import Path - -class ExceptionRepr: - """Representation of an exception for reporting purposes.""" - - def toterminal(self, tw: TerminalWriter) -> None: - """Write exception representation to terminal.""" - - def __str__(self) -> str: - """String representation of exception.""" - -class InvocationParams: - """Parameters used during pytest invocation.""" - - # Attributes - args: tuple[str, ...] # Command line arguments - plugins: tuple[str, ...] | None # Plugin names - dir: Path # Working directory -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/fixtures.md b/.tessl/tiles/tessl/pypi-pytest/docs/fixtures.md deleted file mode 100644 index 369f5ec05..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/fixtures.md +++ /dev/null @@ -1,311 +0,0 @@ -# Fixtures and Dependency Injection - -Comprehensive fixture system for managing test dependencies, setup, and teardown with support for multiple scopes, parametrization, and automatic resolution. The fixture system provides a powerful dependency injection framework that eliminates the need for traditional setUp/tearDown methods. - -## Capabilities - -### Fixture Declaration - -Decorator for marking functions as fixture factories that provide reusable test components. - -```python { .api } -def fixture( - fixture_function=None, - *, - scope: str = "function", - params=None, - autouse: bool = False, - ids=None, - name: str | None = None -): - """ - Mark function as fixture factory. - - Parameters: - - fixture_function: Function to mark as fixture (when used as decorator) - - scope: Fixture scope ("function", "class", "module", "package", "session") - - params: List of parameters for parametrized fixtures - - autouse: Whether fixture runs automatically for all tests - - ids: List of test IDs for parametrized fixtures - - name: Alternative name for the fixture - - Returns: - FixtureFunctionMarker or FixtureFunctionDefinition - """ -``` - -**Usage Example:** - -```python -import pytest -import tempfile -import os - -# Basic fixture -@pytest.fixture -def sample_data(): - return [1, 2, 3, 4, 5] - -# Fixture with teardown -@pytest.fixture -def temp_file(): - fd, path = tempfile.mkstemp() - yield path # This is where the test runs - os.close(fd) - os.unlink(path) - -# Scoped fixture -@pytest.fixture(scope="module") -def database_connection(): - connection = create_db_connection() - yield connection - connection.close() - -# Parametrized fixture -@pytest.fixture(params=[1, 2, 3]) -def number(request): - return request.param - -# Autouse fixture -@pytest.fixture(autouse=True) -def setup_test_environment(): - setup_environment() - yield - cleanup_environment() - -# Named fixture -@pytest.fixture(name="data") -def sample_data_fixture(): - return {"key": "value"} - -def test_with_fixtures(sample_data, temp_file, number, data): - assert len(sample_data) == 5 - assert os.path.exists(temp_file) - assert number in [1, 2, 3] - assert data["key"] == "value" -``` - -### Legacy Fixture Support - -```python { .api } -def yield_fixture(*args, **kwargs): - """ - Legacy alias for fixture decorator (deprecated). - Use pytest.fixture instead. - """ -``` - -### Fixture Request - -Object passed to fixture functions providing access to the requesting test context and fixture management capabilities. - -```python { .api } -class FixtureRequest: - """Request object passed to fixture functions.""" - - # Core attributes - node: Node # Test node that requested the fixture - scope: str # Fixture scope - fixturename: str # Name of the fixture - config: Config # pytest configuration object - function: Callable | None # Test function (if applicable) - cls: type | None # Test class (if applicable) - instance: Any | None # Test class instance (if applicable) - module: Any | None # Test module - param: Any # Current parameter (for parametrized fixtures) - keywords: dict # Test keywords/markers - - def getfixturevalue(self, argname: str) -> Any: - """ - Get value of another fixture by name. - - Parameters: - - argname: Name of fixture to retrieve - - Returns: - Fixture value - """ - - def applymarker(self, marker) -> None: - """ - Apply marker to the test node. - - Parameters: - - marker: Marker to apply - """ - - def raiseerror(self, msg: str | None) -> None: - """ - Raise FixtureLookupError with given message. - - Parameters: - - msg: Error message - """ - - @property - def fspath(self): - """Legacy property for file path (use node.path instead).""" -``` - -**Usage Example:** - -```python -import pytest - -@pytest.fixture -def user_data(request): - # Access test function name - test_name = request.node.name - - # Get configuration - config_value = request.config.getini("test_setting") - - # Access other fixtures - database = request.getfixturevalue("database_connection") - - # Create user based on test context - if "admin" in test_name: - return create_admin_user(database) - else: - return create_regular_user(database) - -@pytest.fixture(params=["sqlite", "postgresql"]) -def database_type(request): - return request.param - -@pytest.fixture -def configured_database(request, database_type): - # Access parameter from parametrized dependency - db_type = database_type - config = request.config - - return setup_database(db_type, config) -``` - -### Fixture Definition - -Container for fixture definitions with metadata and execution logic. - -```python { .api } -class FixtureDef: - """Container for fixture definitions.""" - - # Core attributes - argname: str # Fixture argument name - func: Callable # Fixture function - scope: str # Fixture scope - params: list | None # Parameters for parametrized fixtures - autouse: bool # Whether fixture is automatically used - ids: list | None # Parameter IDs - - # Execution methods (internal use) - def execute(self, request: FixtureRequest): ... - def finish(self, request: FixtureRequest): ... -``` - -### Fixture Exceptions - -Exception raised when fixture cannot be found or resolved. - -```python { .api } -class FixtureLookupError(Exception): - """ - Raised when fixture cannot be found or resolved. - - This exception is raised when: - - A fixture name is not found - - Circular fixture dependencies are detected - - Fixture scope conflicts occur - """ -``` - -## Fixture Scopes - -```python { .api } -# Fixture scope hierarchy (from narrowest to broadest) -FIXTURE_SCOPES = [ - "function", # Default: new instance per test function - "class", # One instance per test class - "module", # One instance per test module - "package", # One instance per test package - "session" # One instance per test session -] -``` - -## Built-in Fixtures - -pytest provides many built-in fixtures automatically available in all tests: - -```python { .api } -# Core built-in fixtures (examples) -def test_with_builtins( - request, # FixtureRequest object - config, # Config object - monkeypatch, # MonkeyPatch object - capfd, # CaptureFixture for file descriptors - capsys, # CaptureFixture for sys.stdout/stderr - caplog, # LogCaptureFixture - tmp_path, # pathlib.Path to temporary directory - tmp_path_factory, # TempPathFactory - pytester, # Pytester for testing pytest plugins - recwarn # WarningsRecorder -): - pass -``` - -## Advanced Fixture Patterns - -### Fixture Dependencies - -```python -@pytest.fixture -def database(): - return setup_database() - -@pytest.fixture -def user(database): - return create_user(database) - -@pytest.fixture -def admin_user(database): - return create_admin_user(database) - -def test_user_permissions(user, admin_user): - assert not user.is_admin - assert admin_user.is_admin -``` - -### Conditional Fixtures - -```python -@pytest.fixture -def storage_backend(request): - if request.config.getoption("--use-redis"): - return RedisStorage() - else: - return MemoryStorage() -``` - -### Factory Fixtures - -```python -@pytest.fixture -def user_factory(): - created_users = [] - - def create_user(**kwargs): - user = User(**kwargs) - created_users.append(user) - return user - - yield create_user - - # Cleanup - for user in created_users: - user.delete() - -def test_multiple_users(user_factory): - user1 = user_factory(name="Alice") - user2 = user_factory(name="Bob") - assert user1.name != user2.name -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/index.md b/.tessl/tiles/tessl/pypi-pytest/docs/index.md deleted file mode 100644 index 376739e8f..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/index.md +++ /dev/null @@ -1,466 +0,0 @@ -# pytest - -A comprehensive Python testing framework that makes it easy to write simple tests while scaling to support complex functional testing for applications and libraries. pytest features detailed assertion introspection using plain assert statements, automatic test discovery, modular fixtures for managing test resources, and a rich plugin architecture with over 1300 external plugins. - -## Package Information - -- **Package Name**: pytest -- **Language**: Python -- **Installation**: `pip install pytest` -- **Version**: 8.4.2 -- **License**: MIT -- **Python Compatibility**: Python 3.9+ (3.9, 3.10, 3.11, 3.12, 3.13, 3.14) - -## Core Imports - -```python -import pytest -``` - -For accessing specific functionality: - -```python -from pytest import ( - fixture, raises, skip, xfail, fail, - mark, param, approx, Config, Session -) -``` - -## Basic Usage - -```python -import pytest - -# Simple test function -def test_basic_math(): - assert 2 + 2 == 4 - assert 5 * 3 == 15 - -# Test with fixture -@pytest.fixture -def sample_data(): - return [1, 2, 3, 4, 5] - -def test_with_fixture(sample_data): - assert len(sample_data) == 5 - assert sum(sample_data) == 15 - -# Parametrized test -@pytest.mark.parametrize("input,expected", [ - (2, 4), - (3, 9), - (4, 16) -]) -def test_square(input, expected): - assert input ** 2 == expected - -# Exception testing -def test_exception(): - with pytest.raises(ValueError): - int("invalid") - -# Skipping tests -@pytest.mark.skip(reason="Not implemented yet") -def test_future_feature(): - pass - -# Expected failure -@pytest.mark.xfail(reason="Known bug") -def test_known_issue(): - assert False -``` - -## Architecture - -pytest's architecture is built around several key components: - -- **Collection System**: Automatic discovery and collection of test files, classes, and functions using a tree-based collector hierarchy -- **Fixture System**: Dependency injection framework providing reusable test components with automatic cleanup and parametrization -- **Plugin Architecture**: Hook-based system enabling extensibility through 1300+ community plugins and custom implementations -- **Configuration Management**: Comprehensive configuration through command-line options, ini files, and programmatic access -- **Assertion Introspection**: Advanced assertion rewriting providing detailed failure information without custom assertion methods -- **Mark System**: Flexible test categorization and parametrization supporting conditional execution and metadata attachment - -This design enables pytest to scale from simple unit tests to complex functional testing scenarios while maintaining ease of use and extensive customization capabilities. - -## Capabilities - -### Core Testing Functions - -Essential functions for controlling test execution flow including skip, xfail, fail, exit, and conditional imports. These functions provide the fundamental building blocks for test control and outcome management. - -```python { .api } -def skip(reason: str = "", *, allow_module_level: bool = False): ... -def xfail(reason: str = ""): ... -def fail(reason: str = "", pytrace: bool = True): ... -def exit(reason: str = "", returncode: int | None = None): ... -def importorskip(modname: str, minversion: str | None = None, reason: str | None = None, *, exc_type: type[ImportError] | None = None): ... -``` - -[Testing Functions](./testing-functions.md) - -### Exception Handling and Assertions - -Context managers and utilities for asserting exceptions, warnings, and approximate numerical comparisons. Includes comprehensive exception information and group exception handling. - -```python { .api } -def raises(expected_exception, *, match=None, check=None) -> RaisesExc: ... -def warns(expected_warning, *, match=None) -> WarningsRecorder: ... -def deprecated_call(func=None, *args, **kwargs): ... -def approx(expected, rel=None, abs=None, nan_ok: bool = False): ... -``` - -[Assertions](./assertions.md) - -### Fixture System and Dependency Injection - -Comprehensive fixture system for managing test dependencies, setup, and teardown with support for multiple scopes, parametrization, and automatic resolution. - -```python { .api } -def fixture(fixture_function=None, *, scope="function", params=None, autouse=False, ids=None, name=None): ... - -class FixtureRequest: - def getfixturevalue(self, argname: str): ... - def applymarker(self, marker): ... -``` - -[Fixtures](./fixtures.md) - -### Configuration and Plugin System - -Central configuration management and plugin architecture enabling extensive customization through hooks, configuration files, and command-line options. - -```python { .api } -class Config: - def getini(self, name: str): ... - def getoption(self, name: str): ... - def addinivalue_line(self, name: str, line: str): ... - -class PytestPluginManager: - def register(self, plugin, name=None): ... - def unregister(self, plugin=None, name=None): ... - -def hookimpl(**kwargs): ... -def hookspec(**kwargs): ... -``` - -[Configuration](./configuration.md) - -### Test Collection and Execution - -Collection tree components for test discovery, organization, and execution including nodes, collectors, items, and session management. - -```python { .api } -class Session: - def collect(self): ... - def pytest_runtest_protocol(self, item): ... - -class Function: - def runtest(self): ... - def setup(self): ... - def teardown(self): ... - -class Metafunc: - def parametrize(self, argnames, argvalues, **kwargs): ... -``` - -[Test Collection](./test-collection.md) - -### Test Utilities and Environment Control - -Utilities for controlling test environments including monkeypatching, output capture, temporary paths, caching, and testing framework testing tools. - -```python { .api } -class MonkeyPatch: - def setattr(self, target, name, value=, raising: bool = True): ... - def setenv(self, name: str, value: str, prepend: str | None = None): ... - def chdir(self, path): ... - -class CaptureFixture: - def readouterr(self): ... - def disabled(self): ... - -class Cache: - def get(self, key: str, default): ... - def set(self, key: str, value: object) -> None: ... - def mkdir(self, name: str) -> Path: ... - -class Stash: - def __setitem__(self, key: StashKey[T], value: T) -> None: ... - def __getitem__(self, key: StashKey[T]) -> T: ... - def get(self, key: StashKey[T], default: D) -> T | D: ... - -class StashKey(Generic[T]): - """Type-safe key for Stash storage.""" - -class Pytester: - def makepyfile(self, **kwargs): ... - def runpytest(self, *args, **kwargs): ... -``` - -[Test Utilities](./test-utilities.md) - -### Marks and Parametrization - -Mark system for test categorization, parametrization, and metadata attachment enabling flexible test selection and data-driven testing. - -```python { .api } -class MarkGenerator: - def parametrize(self, argnames, argvalues, **kwargs): ... - def skip(self, reason: str = ""): ... - def skipif(self, condition, *, reason: str = ""): ... - def xfail(self, condition=True, *, reason: str = "", raises=None, run: bool = True, strict: bool = False): ... - -def param(*values, marks=(), id=None): ... - -# Access via pytest.mark -mark: MarkGenerator -``` - -[Marks and Parametrization](./marks.md) - -### Test Reporting and Results - -Comprehensive reporting system for test execution results, collection reports, and terminal output formatting with detailed information about test outcomes. - -```python { .api } -class TestReport: - def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: ... - -class CollectReport: - def _to_json(self) -> dict[str, Any]: ... - -class CallInfo(Generic[TResult]): - def from_call(cls, func: Callable[[], TResult], when: str, reraise=None) -> CallInfo[TResult]: ... - -class TerminalReporter: - def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: ... - def pytest_runtest_logreport(self, report: TestReport) -> None: ... -``` - -[Test Reporting](./reporting.md) - -### Warning Types and Error Handling - -Comprehensive warning type hierarchy for pytest-specific warnings including deprecation warnings, configuration warnings, and collection warnings. - -```python { .api } -class PytestWarning(UserWarning): ... -class PytestDeprecationWarning(PytestWarning, DeprecationWarning): ... -class PytestConfigWarning(PytestWarning): ... -class PytestCollectionWarning(PytestWarning): ... -class PytestUnknownMarkWarning(PytestWarning): ... -``` - -[Warning Types](./warnings.md) - -## Entry Points - -```python { .api } -def main( - args: list[str] | os.PathLike[str] | None = None, - plugins: Sequence[str | _PluggyPlugin] | None = None, -) -> int | ExitCode: - """ - Main entry point for running pytest programmatically. - - Performs an in-process test run with comprehensive configuration - and plugin management. - - Parameters: - - args: Command line arguments. If None, uses sys.argv. Can be list - of strings or single PathLike object - - plugins: Plugin objects or names to auto-register during initialization - - Returns: - Exit code (0 for success, non-zero for failure/errors) - - Examples: - >>> pytest.main() # Run with default args - >>> pytest.main(['-v', 'tests/']) # With specific arguments - >>> pytest.main(['tests/'], plugins=['my_plugin']) # With plugins - """ - -def console_main() -> int: - """ - Console script entry point for pytest command. - - This is the function called when running 'pytest' from command line. - Not intended for programmatic use - use main() instead. - - Returns: - Integer exit code for console applications - - Note: - Handles BrokenPipeError gracefully and flushes stdout before returning. - """ - -class cmdline: - """Compatibility namespace for legacy pytest usage.""" - - main = staticmethod(main) # Provides pytest.cmdline.main() compatibility -``` - -**Usage Examples:** - -```python -import pytest -from pathlib import Path - -# Basic programmatic usage -def run_tests(): - exit_code = pytest.main(['-v', '--tb=short']) - if exit_code == 0: - print("All tests passed!") - return exit_code - -# With custom configuration -def run_specific_tests(): - args = [ - 'tests/unit/', # Test directory - '-v', # Verbose output - '--maxfail=3', # Stop after 3 failures - '--tb=short', # Short traceback format - '-x' # Stop on first failure - ] - return pytest.main(args) - -# With plugins -def run_with_plugins(): - return pytest.main( - ['tests/'], - plugins=['pytest-html', 'pytest-cov'] - ) - -# Legacy compatibility usage -def legacy_style(): - return pytest.cmdline.main(['-v', 'tests/']) - -# Handling results -def test_runner_with_handling(): - result = pytest.main(['tests/integration/']) - - if result == pytest.ExitCode.OK: - print("Tests passed successfully") - elif result == pytest.ExitCode.TESTS_FAILED: - print("Some tests failed") - elif result == pytest.ExitCode.NO_TESTS_RAN: - print("No tests were collected") - elif result == pytest.ExitCode.INTERRUPTED: - print("Test run was interrupted") - else: - print(f"Test run failed with exit code: {result}") - - return result -``` - -## Types - -```python { .api } -from enum import Enum - -class ExitCode(Enum): - OK = 0 - TESTS_FAILED = 1 - INTERRUPTED = 2 - INTERNAL_ERROR = 3 - USAGE_ERROR = 4 - NO_TESTS_RAN = 5 - -class UsageError(Exception): - """Raised for command-line usage errors.""" - -class FixtureLookupError(Exception): - """Raised when fixture cannot be found or resolved.""" -``` - -## Built-in Fixtures - -pytest provides many built-in fixtures automatically available in all tests: - -```python { .api } -# Core configuration and request -def test_core_fixtures( - request, # FixtureRequest: Access to test context and other fixtures - config, # Config: pytest configuration object - pytestconfig, # Config: Alias for config fixture -): - pass - -# Output capture -def test_capture_fixtures( - capsys, # CaptureFixture: Capture sys.stdout/stderr (text) - capsysbinary, # CaptureFixture: Capture sys.stdout/stderr (binary) - capfd, # CaptureFixture: Capture file descriptors 1/2 (text) - capfdbinary, # CaptureFixture: Capture file descriptors 1/2 (binary) - caplog, # LogCaptureFixture: Capture log messages -): - pass - -# Environment modification -def test_environment_fixtures( - monkeypatch, # MonkeyPatch: Modify objects, environment, sys.path -): - pass - -# Temporary paths -def test_temp_fixtures( - tmp_path, # pathlib.Path: Temporary directory (function scope) - tmp_path_factory, # TempPathFactory: Create additional temp directories - tmpdir, # py.path.local: Legacy temp directory (deprecated) - tmpdir_factory, # TempdirFactory: Legacy temp factory (deprecated) -): - pass - -# Warning handling -def test_warning_fixtures( - recwarn, # WarningsRecorder: Record warnings during test -): - pass - -# Plugin testing (requires pytest plugin) -def test_plugin_fixtures( - pytester, # Pytester: Test pytest plugins and functionality - testdir, # Testdir: Legacy plugin testing (deprecated) -): - pass - -# Doctest integration (requires doctest plugin) -def test_doctest_fixtures( - doctest_namespace, # dict: Namespace for doctest execution -): - pass -``` - -## Version Information - -```python { .api } -__version__: str # Current pytest version string (e.g., "8.4.2") -version_tuple: tuple[int, ...] # Version as tuple of integers (e.g., (8, 4, 2)) -``` - -**Usage Examples:** - -```python -import pytest - -# Check pytest version -print(f"pytest version: {pytest.__version__}") -# Output: pytest version: 8.4.2 - -# Version tuple for programmatic comparison -version = pytest.version_tuple -if version >= (8, 0, 0): - print("Using modern pytest features") - -# Version checking utility -def require_pytest_version(min_version_tuple): - if pytest.version_tuple < min_version_tuple: - raise RuntimeError( - f"pytest {'.'.join(map(str, min_version_tuple))} or higher required, " - f"found {pytest.__version__}" - ) - -# Example usage -require_pytest_version((7, 0, 0)) # Ensure pytest 7.0.0+ -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/marks.md b/.tessl/tiles/tessl/pypi-pytest/docs/marks.md deleted file mode 100644 index b9629daf5..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/marks.md +++ /dev/null @@ -1,364 +0,0 @@ -# Marks and Parametrization - -Mark system for test categorization, parametrization, and metadata attachment enabling flexible test selection and data-driven testing. The mark system provides a powerful way to organize and control test execution. - -## Capabilities - -### Mark Generation - -Dynamic mark creation system providing access to built-in and custom marks. - -```python { .api } -class MarkGenerator: - """Generates mark decorators dynamically.""" - - def __getattr__(self, name: str) -> MarkDecorator: - """Create MarkDecorator for any mark name.""" - - # Built-in marks - def parametrize( - self, - argnames: str | list[str] | tuple[str, ...], - argvalues, - *, - indirect: bool | list[str] = False, - ids=None, - scope: str | None = None - ) -> MarkDecorator: - """Parametrize test function.""" - - def skip(self, reason: str = "") -> MarkDecorator: - """Skip test unconditionally.""" - - def skipif( - self, - condition, - *, - reason: str = "" - ) -> MarkDecorator: - """Skip test conditionally.""" - - def xfail( - self, - condition=True, - *, - reason: str = "", - raises=None, - run: bool = True, - strict: bool = False - ) -> MarkDecorator: - """Mark test as expected to fail.""" - - def usefixtures(self, *names: str) -> MarkDecorator: - """Use fixtures without declaring them as arguments.""" - - def filterwarnings(self, *filters: str) -> MarkDecorator: - """Filter warnings for specific test.""" - -# Global mark instance -mark: MarkGenerator -``` - -**Usage Example:** - -```python -import pytest - -# Basic marks -@pytest.mark.slow -def test_slow_operation(): - pass - -@pytest.mark.skip(reason="Not implemented yet") -def test_future_feature(): - pass - -@pytest.mark.skipif(sys.version_info < (3, 9), reason="Requires Python 3.9+") -def test_new_feature(): - pass - -@pytest.mark.xfail(reason="Known bug") -def test_known_issue(): - assert False - -# Custom marks -@pytest.mark.integration -@pytest.mark.database -def test_database_integration(): - pass - -# Using fixtures without parameters -@pytest.mark.usefixtures("setup_test_data", "cleanup_temp_files") -def test_with_automatic_fixtures(): - pass - -# Filter warnings -@pytest.mark.filterwarnings("ignore::DeprecationWarning") -def test_legacy_code(): - pass -``` - -### Mark Decorators - -Decorators that apply marks to test functions and classes. - -```python { .api } -class MarkDecorator: - """Decorator that applies marks to test functions and classes.""" - - # Attributes - mark: Mark # The underlying mark - markname: str # Mark name - args: tuple # Mark arguments - kwargs: dict # Mark keyword arguments - - def __call__(self, func_or_class): - """Apply mark to function or class.""" - - def with_args(self, *args, **kwargs) -> MarkDecorator: - """Create new decorator with additional arguments.""" - - def combine(self, other: MarkDecorator) -> MarkDecorator: - """Combine with another mark decorator.""" -``` - -### Mark Objects - -Represents a mark applied to a test with its arguments and metadata. - -```python { .api } -class Mark: - """Represents a mark applied to a test.""" - - # Attributes - name: str # Mark name - args: tuple # Mark arguments - kwargs: dict # Mark keyword arguments - - def combined_with(self, other: Mark) -> Mark: - """Combine with another mark.""" - - def _for_parametrize(self) -> Mark: - """Create mark suitable for parametrization.""" -``` - -### Test Parametrization - -Create parameter sets for data-driven testing. - -```python { .api } -def param(*values, marks=(), id=None): - """ - Create parameter sets for pytest.mark.parametrize. - - Parameters: - - values: Parameter values for the test - - marks: Marks to apply to this parameter set - - id: Identifier for this parameter set - - Returns: - ParameterSet object - """ - -class ParameterSet: - """Represents a set of parameters for parametrized tests.""" - - # Attributes - values: tuple # Parameter values - marks: tuple[Mark, ...] # Applied marks - id: str | None # Parameter set identifier - - @classmethod - def param(cls, *values, marks=(), id=None) -> ParameterSet: - """Create parameter set.""" - - @classmethod - def extract_from( - cls, - parameterset, - legacy_force_tuple: bool = False - ) -> ParameterSet: - """Extract parameter set from various input formats.""" -``` - -**Usage Example:** - -```python -import pytest - -# Basic parametrization -@pytest.mark.parametrize("input,expected", [ - (2, 4), - (3, 9), - (4, 16), -]) -def test_square(input, expected): - assert input ** 2 == expected - -# Named parameters with IDs -@pytest.mark.parametrize( - "operation,a,b,expected", - [ - ("add", 2, 3, 5), - ("multiply", 4, 5, 20), - ("subtract", 10, 3, 7), - ], - ids=["addition", "multiplication", "subtraction"] -) -def test_math_operations(operation, a, b, expected): - if operation == "add": - assert a + b == expected - elif operation == "multiply": - assert a * b == expected - elif operation == "subtract": - assert a - b == expected - -# Using pytest.param with marks -@pytest.mark.parametrize("value", [ - 1, - 2, - pytest.param(3, marks=pytest.mark.xfail(reason="Known issue with 3")), - pytest.param(4, marks=pytest.mark.skip(reason="Skip 4 for now")), - pytest.param(5, id="special_five"), -]) -def test_values(value): - assert value < 10 - -# Multiple parametrizations -@pytest.mark.parametrize("browser", ["chrome", "firefox"]) -@pytest.mark.parametrize("os", ["windows", "linux", "mac"]) -def test_cross_platform(browser, os): - # Runs 6 times (2 browsers × 3 operating systems) - pass - -# Indirect parametrization (parameters passed to fixtures) -@pytest.fixture -def database(request): - db_type = request.param - return setup_database(db_type) - -@pytest.mark.parametrize("database", ["sqlite", "postgresql"], indirect=True) -def test_with_database(database): - # database fixture receives "sqlite" or "postgresql" as request.param - pass -``` - -### Mark Constants - -Special constants for controlling mark behavior. - -```python { .api } -HIDDEN_PARAM = object() # Special value to hide parameters from test names -``` - -**Usage Example:** - -```python -@pytest.mark.parametrize("value,description", [ - (1, "first"), - (2, "second"), - (pytest.HIDDEN_PARAM, "hidden"), # This parameter won't appear in test name -]) -def test_with_hidden_param(value, description): - pass -``` - -## Built-in Marks - -### Skip Marks - -```python -# Unconditional skip -@pytest.mark.skip(reason="Not implemented") - -# Conditional skip -@pytest.mark.skipif(condition, reason="Explanation") -@pytest.mark.skipif(sys.platform == "win32", reason="Unix only") -@pytest.mark.skipif(not has_feature(), reason="Feature not available") -``` - -### Expected Failure Marks - -```python -# Basic expected failure -@pytest.mark.xfail(reason="Known bug") - -# Conditional expected failure -@pytest.mark.xfail(sys.version_info < (3, 9), reason="Requires Python 3.9+") - -# Strict xfail (failure if test unexpectedly passes) -@pytest.mark.xfail(strict=True, reason="Should definitely fail") - -# Expected specific exception -@pytest.mark.xfail(raises=ValueError, reason="Should raise ValueError") - -# Don't run, just mark as xfail -@pytest.mark.xfail(run=False, reason="Don't run this test") -``` - -### Parametrization Marks - -```python -# Simple parametrization -@pytest.mark.parametrize("value", [1, 2, 3]) - -# Multiple parameters -@pytest.mark.parametrize("a,b,expected", [(1, 2, 3), (2, 3, 5)]) - -# Custom IDs -@pytest.mark.parametrize("value", [1, 2, 3], ids=["one", "two", "three"]) - -# Indirect parametrization -@pytest.mark.parametrize("fixture_name", ["value1", "value2"], indirect=True) -``` - -### Fixture Marks - -```python -# Use fixtures without declaring as parameters -@pytest.mark.usefixtures("setup_database", "cleanup_temp_files") - -# Filter warnings -@pytest.mark.filterwarnings("ignore::DeprecationWarning") -@pytest.mark.filterwarnings("error::UserWarning") -``` - -## Running Tests with Marks - -Command-line options for running specific marked tests: - -```bash -# Run only tests with specific mark -pytest -m "slow" - -# Run tests with multiple marks -pytest -m "slow and integration" - -# Run tests without specific mark -pytest -m "not slow" - -# Complex mark expressions -pytest -m "(slow or integration) and not windows" -``` - -## Custom Marks - -Register custom marks in pytest configuration: - -```python -# pytest.ini -[tool:pytest] -markers = - slow: marks tests as slow (deselect with '-m "not slow"') - integration: marks tests as integration tests - unit: marks tests as unit tests - smoke: marks tests as smoke tests - regression: marks tests as regression tests - -# pyproject.toml -[tool.pytest.ini_options] -markers = [ - "slow: marks tests as slow", - "integration: marks tests as integration tests", -] -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/reporting.md b/.tessl/tiles/tessl/pypi-pytest/docs/reporting.md deleted file mode 100644 index 4b3c814e3..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/reporting.md +++ /dev/null @@ -1,538 +0,0 @@ -# Test Reporting and Results - -Comprehensive reporting system for test execution results, collection reports, and terminal output formatting. These classes provide detailed information about test outcomes and handle presentation of results to users. - -## Capabilities - -### Test Execution Reports - -Classes representing the results of test execution. - -```python { .api } -@dataclasses.dataclass -class CallInfo(Generic[TResult]): - """Result/Exception info of a function invocation.""" - - def __init__( - self, - result: TResult | None, - excinfo: ExceptionInfo[BaseException] | None, - start: float, - stop: float, - duration: float, - when: Literal["collect", "setup", "call", "teardown"], - _ispytest: bool = False, - ) -> None: - """ - Initialize call information. - - Parameters: - - result: Return value (None if exception occurred) - - excinfo: Exception info if raised - - start: Start time (seconds since epoch) - - stop: End time (seconds since epoch) - - duration: Call duration in seconds - - when: Invocation context - - _ispytest: Internal pytest flag - """ - - # Attributes - _result: TResult | None # Private result storage - excinfo: ExceptionInfo[BaseException] | None # Captured exception - start: float # Start timestamp - stop: float # End timestamp - duration: float # Execution duration - when: str # Context of invocation ("collect", "setup", "call", "teardown") - - @property - def result(self) -> TResult: - """ - Get the call result. - - Returns: - The function result - - Raises: - AttributeError: If excinfo is not None (exception occurred) - """ - - @classmethod - def from_call( - cls, - func: Callable[[], TResult], - when: str, - reraise=None - ) -> CallInfo[TResult]: - """ - Execute function and capture call information. - - Parameters: - - func: Function to execute - - when: Context identifier - - reraise: Exception types to reraise immediately - - Returns: - CallInfo with execution results and timing - """ - -class TestReport(BaseReport): - """Basic test report object for test execution results. - - Also used for setup and teardown calls if they fail. - Reports can contain arbitrary extra attributes. - """ - - def __init__( - self, - nodeid: str, - location: tuple[str, int | None, str], - keywords: Mapping[str, Any], - outcome: Literal["passed", "failed", "skipped"], - longrepr: None | ExceptionInfo | tuple[str, int, str] | str | TerminalRepr, - when: Literal["setup", "call", "teardown"], - sections: Iterable[tuple[str, str]] = (), - duration: float = 0, - start: float = 0, - stop: float = 0, - user_properties: Iterable[tuple[str, object]] | None = None, - **extra - ) -> None: - """ - Initialize test report. - - Parameters: - - nodeid: Normalized collection nodeid - - location: File path, line number, domain info - - keywords: Name->value dict of keywords/markers - - outcome: Test outcome ("passed", "failed", "skipped") - - longrepr: Failure representation - - when: Test phase ("setup", "call", "teardown") - - sections: Extra information tuples (heading, content) - - duration: Test execution time in seconds - - start: Start time (seconds since epoch) - - stop: End time (seconds since epoch) - - user_properties: User-defined properties - - **extra: Additional arbitrary attributes - """ - - # Core attributes - nodeid: str # Test identifier - location: tuple[str, int | None, str] # Test location info - keywords: Mapping[str, Any] # Associated keywords and markers - outcome: str # One of "passed", "failed", "skipped" - longrepr # Failure representation (None for passed tests) - when: str # Phase: "setup", "call", or "teardown" - user_properties: list[tuple[str, object]] # User-defined test properties - sections: list[tuple[str, str]] # Captured output sections - duration: float # Execution duration - start: float # Start timestamp - stop: float # End timestamp - wasxfail: str # XFail reason (if applicable) - - @classmethod - def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: - """ - Create TestReport from test item and call info. - - Parameters: - - item: Test item that was executed - - call: Call information from execution - - Returns: - TestReport with complete execution information - """ - - def _to_json(self) -> dict[str, Any]: - """ - Serialize report to JSON-compatible dict. - - Returns: - Dictionary representation for JSON serialization - """ - - @classmethod - def _from_json(cls, reportdict: dict[str, object]) -> TestReport: - """ - Deserialize TestReport from JSON dict. - - Parameters: - - reportdict: Dictionary from JSON deserialization - - Returns: - TestReport instance - """ - -class CollectReport(BaseReport): - """Collection report object representing collection results. - - Reports can contain arbitrary extra attributes. - """ - - def __init__( - self, - nodeid: str, - outcome: Literal["passed", "failed", "skipped"], - longrepr: None | ExceptionInfo | tuple[str, int, str] | str | TerminalRepr, - result: list[Item | Collector] | None, - sections: Iterable[tuple[str, str]] = (), - **extra - ) -> None: - """ - Initialize collection report. - - Parameters: - - nodeid: Normalized collection nodeid - - outcome: Collection outcome ("passed", "failed", "skipped") - - longrepr: Failure representation - - result: Collected items and nodes - - sections: Extra information sections - - **extra: Additional arbitrary attributes - """ - - # Attributes - when = "collect" # Always "collect" for collection reports - nodeid: str # Collection node identifier - outcome: str # Collection result - longrepr # Collection failure details (if any) - result: list[Item | Collector] # Successfully collected items - sections: list[tuple[str, str]] # Captured output during collection - - @property - def location(self) -> tuple[str, None, str]: - """ - Get collection location. - - Returns: - Tuple of (fspath, None, fspath) - """ - - def _to_json(self) -> dict[str, Any]: - """ - Serialize report to JSON-compatible dict. - - Returns: - Dictionary representation for JSON serialization - """ - - @classmethod - def _from_json(cls, reportdict: dict[str, object]) -> CollectReport: - """ - Deserialize CollectReport from JSON dict. - - Parameters: - - reportdict: Dictionary from JSON deserialization - - Returns: - CollectReport instance - """ -``` - -### Terminal Output and Formatting - -Classes controlling terminal output during test execution. - -```python { .api } -class TestShortLogReport(NamedTuple): - """Container for test status information used in terminal reporting. - - Used to store the test status result category, shortletter and verbose word. - For example "rerun", "R", ("RERUN", {"yellow": True}). - """ - - category: str # Result class ("passed", "skipped", "error", or empty) - letter: str # Short letter for progress (".", "s", "E", or empty) - word: str | tuple[str, Mapping[str, bool]] # Verbose word or (word, markup) tuple - -class TerminalReporter: - """Controls terminal output formatting and reporting during test execution. - - Main class responsible for all terminal output including progress indicators, - test results, summaries, and formatting. - """ - - def __init__(self, config: Config, file: TextIO | None = None) -> None: - """ - Initialize terminal reporter. - - Parameters: - - config: pytest configuration object - - file: Output stream (defaults to sys.stdout) - """ - - # Core attributes - config: Config # pytest configuration - stats: dict[str, list[Any]] # Statistics by category (failed, passed, etc.) - _tw: TerminalWriter # Terminal writer for output - reportchars: str # Characters controlling what to report - hasmarkup: bool # Whether terminal supports markup - isatty: bool # Whether output is to a terminal - verbosity: int # Verbosity level - showfspath: bool # Whether to show file paths - showlongtestinfo: bool # Whether to show detailed test info - _session: Session | None # Current test session - _numcollected: int # Number of collected items - currentfspath: Path | str | int | None # Current file being processed - - def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: - """ - Write content to terminal. - - Parameters: - - content: Content to write - - flush: Whether to flush output immediately - - **markup: Markup options (bold, red, etc.) - """ - - def write_line(self, line: str | bytes, **markup: bool) -> None: - """ - Write line with newline to terminal. - - Parameters: - - line: Line content - - **markup: Markup options - """ - - def write_sep(self, sep: str, title: str | None = None, **markup: bool) -> None: - """ - Write separator line. - - Parameters: - - sep: Separator character - - title: Optional title to include - - **markup: Markup options - """ - - def rewrite(self, line: str, **markup: bool) -> None: - """ - Rewrite current line (for progress indicators). - - Parameters: - - line: New line content - - **markup: Markup options - """ - - def section(self, title: str, sep: str = "=", **kw: bool) -> None: - """ - Write section header. - - Parameters: - - title: Section title - - sep: Separator character - - **kw: Keyword arguments for formatting - """ - - def pytest_runtest_logreport(self, report: TestReport) -> None: - """ - Handle test report logging. - - Parameters: - - report: Test execution report - """ - - def pytest_collectreport(self, report: CollectReport) -> None: - """ - Handle collection report logging. - - Parameters: - - report: Collection report - """ - - def pytest_sessionstart(self, session: Session) -> None: - """ - Handle session start. - - Parameters: - - session: Test session - """ - - def pytest_sessionfinish(self, session: Session, exitstatus: int | ExitCode) -> None: - """ - Handle session finish. - - Parameters: - - session: Test session - - exitstatus: Exit status code - """ - - def summary_failures(self) -> None: - """Show failure summary section.""" - - def summary_errors(self) -> None: - """Show error summary section.""" - - def summary_warnings(self) -> None: - """Show warning summary section.""" - - def short_test_summary(self) -> None: - """Show short test summary section.""" - - def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]: - """ - Build final statistics line. - - Returns: - Tuple of (stat_parts, duration_string) - """ -``` - -## Usage Examples - -### Creating Custom Reports - -```python -import pytest -from _pytest.reports import TestReport, CollectReport - -# Custom plugin to process test reports -class CustomReportPlugin: - def __init__(self): - self.test_results = [] - - def pytest_runtest_logreport(self, report: TestReport): - # Process test reports - if report.when == "call": # Only process main test execution - self.test_results.append({ - 'nodeid': report.nodeid, - 'outcome': report.outcome, - 'duration': report.duration, - 'location': report.location, - 'user_properties': report.user_properties, - }) - - def pytest_collection_finish(self, session): - # Process collection results - for item in session.items: - print(f"Collected test: {item.nodeid}") - -# Register plugin -pytest.main(["-p", "no:terminal", "--tb=no"], plugins=[CustomReportPlugin()]) -``` - -### Custom Terminal Output - -```python -from _pytest.terminal import TerminalReporter - -class ColorfulReporter(TerminalReporter): - def pytest_runtest_logreport(self, report): - # Custom colored output - if report.when == "call": - if report.outcome == "passed": - self.write("✓ ", green=True) - elif report.outcome == "failed": - self.write("✗ ", red=True) - elif report.outcome == "skipped": - self.write("- ", yellow=True) - - self.write_line(f"{report.nodeid}") - - # Call parent implementation for other functionality - super().pytest_runtest_logreport(report) - -# Use custom reporter -def pytest_configure(config): - if config.pluginmanager.get_plugin("terminalreporter"): - config.pluginmanager.unregister(name="terminalreporter") - - config.pluginmanager.register(ColorfulReporter(config), "terminalreporter") -``` - -### Accessing Call Information - -```python -from _pytest.runner import CallInfo - -def test_with_call_info(): - # CallInfo is typically used internally, but can be accessed in hooks - def example_function(): - return "result" - - # Capture call information - call_info = CallInfo.from_call(example_function, when="call") - - assert call_info.result == "result" - assert call_info.excinfo is None - assert call_info.duration > 0 - assert call_info.when == "call" - -# Plugin to access call information in hooks -def pytest_runtest_call(pyfuncitem): - # This hook has access to CallInfo through the test execution - pass - -def pytest_runtest_makereport(item, call): - # call parameter is a CallInfo object - if call.when == "call" and call.excinfo is None: - print(f"Test {item.nodeid} passed in {call.duration:.2f}s") -``` - -## Integration with Reporting Tools - -### JSON Report Generation - -```python -import json -from _pytest.reports import TestReport, CollectReport - -class JSONReporter: - def __init__(self): - self.reports = [] - - def pytest_runtest_logreport(self, report: TestReport): - # Convert TestReport to JSON-serializable format - report_data = report._to_json() - self.reports.append(report_data) - - def pytest_sessionfinish(self, session): - # Write JSON report - with open("test_report.json", "w") as f: - json.dump(self.reports, f, indent=2) - -# Usage -pytest.main(["--tb=short"], plugins=[JSONReporter()]) -``` - -### Database Integration - -```python -import sqlite3 -from _pytest.reports import TestReport - -class DatabaseReporter: - def __init__(self): - self.conn = sqlite3.connect("test_results.db") - self.setup_database() - - def setup_database(self): - self.conn.execute(""" - CREATE TABLE IF NOT EXISTS test_results ( - nodeid TEXT, - outcome TEXT, - duration REAL, - when_phase TEXT, - timestamp REAL, - location_file TEXT, - location_line INTEGER - ) - """) - - def pytest_runtest_logreport(self, report: TestReport): - if report.when == "call": - self.conn.execute(""" - INSERT INTO test_results - (nodeid, outcome, duration, when_phase, timestamp, location_file, location_line) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, ( - report.nodeid, - report.outcome, - report.duration, - report.when, - report.start, - report.location[0], - report.location[1] - )) - self.conn.commit() - -# Usage with database integration -pytest.main(["-v"], plugins=[DatabaseReporter()]) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/test-collection.md b/.tessl/tiles/tessl/pypi-pytest/docs/test-collection.md deleted file mode 100644 index 5fe3fdc7a..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/test-collection.md +++ /dev/null @@ -1,528 +0,0 @@ -# Test Collection and Execution - -Collection tree components for test discovery, organization, and execution. The collection system builds a hierarchical tree of collectors and items that represent the structure of your test suite. - -## Capabilities - -### Base Collection Classes - -Foundation classes for all collection tree components. - -```python { .api } -class Node: - """Base class for all collection tree components.""" - - # Core attributes - name: str # Node name - parent: Node | None # Parent node - config: Config # pytest configuration - session: Session # Test session - path: Path # File system path - nodeid: str # Unique node identifier - - @classmethod - def from_parent(cls, parent: Node, **kwargs): - """Create node with parent reference.""" - - def listchain(self) -> list[Node]: - """Return list of all parent nodes.""" - - def add_marker(self, marker) -> None: - """Add marker to this node.""" - - def iter_markers(self, name: str | None = None): - """Iterate over markers.""" - - def get_closest_marker(self, name: str): - """Get closest marker with given name.""" - -class Collector(Node, abc.ABC): - """Base class for collection tree internal nodes. - - Collectors create children through collect() and iteratively build - the collection tree. - """ - - class CollectError(Exception): - """An error during collection, contains a custom message.""" - - @abc.abstractmethod - def collect(self) -> Iterable[Item | Collector]: - """ - Collect children (items and collectors) for this collector. - - Returns: - Iterable of Item or Collector objects - """ - - def repr_failure(self, excinfo: ExceptionInfo[BaseException]) -> str | TerminalRepr: - """ - Return a representation of a collection failure. - - Parameters: - - excinfo: Exception information for the failure - - Returns: - String or TerminalRepr representation of the failure - """ - -class Item(Node, abc.ABC): - """Base class for collection tree leaf nodes (test items). - - Note that for a single function there might be multiple test invocation items. - """ - - nextitem = None # Reference to next item - - def __init__( - self, - name, - parent=None, - config: Config | None = None, - session: Session | None = None, - nodeid: str | None = None, - **kw, - ) -> None: - """ - Initialize test item. - - Parameters: - - name: Item name - - parent: Parent collector - - config: pytest configuration - - session: Test session - - nodeid: Node identifier - """ - - @abc.abstractmethod - def runtest(self) -> None: - """ - Run the test case (abstract method). - - Must be implemented by subclasses to execute the actual test. - """ - - def setup(self) -> None: - """Set up test execution.""" - - def teardown(self) -> None: - """Tear down after test execution.""" - - def add_report_section(self, when: str, key: str, content: str) -> None: - """ - Add report section to test results. - - Parameters: - - when: Test phase ("setup", "call", "teardown") - - key: Section identifier - - content: Section content - """ - - def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: - """ - Return location information for reporting. - - Returns: - Tuple of (filename, line_number, test_name) - """ - - @property - def location(self) -> tuple[str, int | None, str]: - """Get test location as (relfspath, lineno, testname).""" - - # Attributes - user_properties: list[tuple[str, object]] # User-defined properties - _report_sections: list[tuple[str, str, str]] # Report sections -``` - -### File System Collectors - -Collectors for file system entities. - -```python { .api } -class FSCollector(Collector, abc.ABC): - """Base class for filesystem-based collectors.""" - - def __init__( - self, - path: Path, - parent: Collector | None = None, - config: Config | None = None, - session: Session | None = None, - nodeid: str | None = None, - ) -> None: - """Initialize filesystem collector with path.""" - -class File(FSCollector, abc.ABC): - """Base class for collecting tests from files.""" - - @abc.abstractmethod - def collect(self) -> Iterable[Item | Collector]: - """Collect tests from file (abstract method).""" - -class Directory(FSCollector, abc.ABC): - """Base class for collecting from directories.""" - - @abc.abstractmethod - def collect(self) -> Iterable[Item | Collector]: - """Collect from directory contents (abstract method).""" - -class Dir(Directory): - """Directory collector implementation. - - Used for directories without __init__.py files. - """ - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - """ - Collect files and subdirectories. - - Returns: - Iterable of collectors for Python files and subdirectories - """ - - @classmethod - def from_parent(cls, parent: nodes.Collector, *, path: Path) -> Self: - """ - Create Dir collector from parent. - - Parameters: - - parent: Parent collector - - path: Directory path - - Returns: - Dir collector instance - """ -``` - -### Python-Specific Collectors - -Collectors specialized for Python test code. - -```python { .api } -class PyCollector(Collector, abc.ABC): - """Base class for Python-specific collectors.""" - - def _getobj(self): - """Import and return the Python object.""" - -class Module(nodes.File, PyCollector): - """Collector for test classes and functions in Python modules.""" - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - """ - Collect test classes and functions from module. - - Returns: - Iterable of Class collectors and Function items - """ - - def _getobj(self): - """ - Import and return the module object. - - Returns: - The imported module - """ - - def _register_setup_module_fixture(self) -> None: - """Register setup/teardown module fixtures.""" - - def _register_setup_function_fixture(self) -> None: - """Register setup/teardown function fixtures.""" - -class Package(nodes.Directory): - """Collector for Python packages (directories with __init__.py).""" - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - """ - Collect modules and subpackages. - - Returns: - Iterable of Module and Package collectors - """ - - def setup(self) -> None: - """Set up package-level fixtures.""" - -class Class(PyCollector): - """Collector for test methods (and nested classes) in Python classes.""" - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - """ - Collect test methods from class. - - Returns: - Iterable of Function items and nested Class collectors - """ - - def newinstance(self): - """ - Create new instance of the class. - - Returns: - New instance of the test class - """ - - @classmethod - def from_parent(cls, parent, *, name, obj=None, **kw) -> Self: - """ - Create Class collector from parent. - - Parameters: - - parent: Parent collector - - name: Class name - - obj: Class object (optional) - - Returns: - Class collector instance - """ - - def _register_setup_class_fixture(self) -> None: - """Register class-level setup/teardown fixtures.""" - - def _register_setup_method_fixture(self) -> None: - """Register method-level setup/teardown fixtures.""" - -class Function(Item): - """Item for Python test functions.""" - - # Core attributes - function: Callable # Test function object - fixturenames: frozenset[str] # Required fixture names - callspec: CallSpec2 | None # Parametrization info - - def runtest(self) -> None: - """Execute the test function.""" - - def setup(self) -> None: - """Set up fixtures and test environment.""" - - def teardown(self) -> None: - """Tear down fixtures and clean up.""" -``` - -### Session Management - -Root-level session management for test execution coordination. - -```python { .api } -class Session(Collector): - """Root of collection tree, collects initial paths.""" - - def collect(self) -> list[Node]: - """Collect from initial file paths.""" - - def pytest_runtest_protocol(self, item: Item) -> bool: - """Run test protocol for item.""" - - def pytest_collection_modifyitems(self, items: list[Item]) -> None: - """Modify collected items.""" -``` - -### Test Parametrization - -Metafunc object for parametrizing tests during collection. - -```python { .api } -class Metafunc: - """Used in pytest_generate_tests hook for parametrizing tests.""" - - # Core attributes - function: Callable # Test function - module: Any # Test module - cls: type | None # Test class (if applicable) - config: Config # pytest configuration - definition: FunctionDefinition # Function definition - fixturenames: frozenset[str] # Fixture names used by function - - def parametrize( - self, - argnames: str | list[str] | tuple[str, ...], - argvalues, - *, - indirect: bool | list[str] = False, - ids=None, - scope: str | None = None - ) -> None: - """Parametrize test function.""" - - def addcall(self, **kwargs) -> None: - """Add individual test call (deprecated, use parametrize).""" -``` - -**Usage Example:** - -```python -# conftest.py -def pytest_generate_tests(metafunc): - if "browser" in metafunc.fixturenames: - metafunc.parametrize( - "browser", - ["chrome", "firefox", "safari"], - ids=["Chrome", "Firefox", "Safari"] - ) - - if "database" in metafunc.fixturenames: - metafunc.parametrize( - "database", - [("sqlite", ":memory:"), ("postgresql", "test_db")], - ids=["SQLite", "PostgreSQL"] - ) - -# Test will run 6 times (3 browsers × 2 databases) -def test_web_app(browser, database): - pass -``` - -### Doctest Integration - -Support for running doctests as part of the test suite. - -```python { .api } -class DoctestItem(Item): - """Test item for executing doctests.""" - - def __init__( - self, - name: str, - parent: DoctestTextfile | DoctestModule, - runner: doctest.DocTestRunner, - dtest: doctest.DocTest, - ) -> None: - """ - Initialize doctest item. - - Parameters: - - name: Item name - - parent: Parent collector (DoctestTextfile or DoctestModule) - - runner: Doctest runner - - dtest: Doctest object - """ - - # Attributes - runner: doctest.DocTestRunner # Doctest runner - dtest: doctest.DocTest # Doctest object - obj = None # No underlying Python object - _fixtureinfo: fixtures.FuncFixtureInfo # Fixture information - fixturenames: frozenset[str] # Required fixture names - - def runtest(self) -> None: - """ - Execute the doctest. - - Runs the doctest using the configured runner and handles - any failures according to pytest conventions. - """ - - def setup(self) -> None: - """Set up doctest fixtures and environment.""" - - def repr_failure(self, excinfo: ExceptionInfo[BaseException]) -> str | TerminalRepr: - """ - Format doctest failures for display. - - Parameters: - - excinfo: Exception information - - Returns: - Formatted failure representation - """ - - def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: - """ - Get doctest location information for reporting. - - Returns: - Tuple of (filename, line_number, doctest_name) - """ - - @classmethod - def from_parent( - cls, - parent: DoctestTextfile | DoctestModule, - *, - name: str, - runner: doctest.DocTestRunner, - dtest: doctest.DocTest, - ) -> Self: - """ - Create DoctestItem from parent collector. - - Parameters: - - parent: Parent collector - - name: Item name - - runner: Doctest runner - - dtest: Doctest object - - Returns: - DoctestItem instance - """ -``` - -## Collection Process - -The collection process follows these steps: - -1. **Start Collection**: Session.collect() begins from initial paths -2. **Directory Collection**: Dir collectors process directories -3. **File Collection**: Module collectors process Python files -4. **Class Collection**: Class collectors process test classes -5. **Function Collection**: Function collectors process test functions -6. **Parametrization**: Metafunc.parametrize() creates multiple test variants -7. **Item Modification**: pytest_collection_modifyitems hook allows final changes - -**Usage Example:** - -```python -# Custom collector example -class YamlFile(pytest.File): - def collect(self): - # Parse YAML file and create test items - raw = yaml.safe_load(self.path.open()) - for name, spec in raw.items(): - yield YamlTest.from_parent(self, name=name, spec=spec) - -class YamlTest(pytest.Item): - def __init__(self, name, parent, spec): - super().__init__(name, parent) - self.spec = spec - - def runtest(self): - # Execute test based on YAML spec - pass - -def pytest_collect_file(parent, path): - if path.suffix == ".yaml" and path.name.startswith("test_"): - return YamlFile.from_parent(parent, path=path) -``` - -## Types - -```python { .api } -from typing import Any, Callable -from pathlib import Path - -class CallSpec2: - """Represents parametrized test call specification.""" - - # Attributes - params: dict[str, Any] # Parameter values - funcargs: dict[str, Any] # Function arguments - id: str | None # Parameter set ID - marks: list[Mark] # Applied marks - - def setmulti(self, **kwargs) -> None: - """Set multiple parameters.""" - -class FunctionDefinition: - """Definition of a test function for collection.""" - - # Attributes - name: str # Function name - obj: Callable # Function object - parent: Node # Parent collector - - def getparent(self, cls: type) -> Node | None: - """Get parent of specific type.""" -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md b/.tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md deleted file mode 100644 index b4fab95d3..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/test-utilities.md +++ /dev/null @@ -1,219 +0,0 @@ -# Test Utilities and Environment Control - -Utilities for controlling test environments including monkeypatching, output capture, temporary paths, and specialized tools for testing pytest plugins. These utilities provide comprehensive control over the test execution environment. - -## Capabilities - -### Environment Modification - -MonkeyPatch provides safe, temporary modifications to objects, environment variables, and system paths. - -```python { .api } -class MonkeyPatch: - """Helper for temporarily modifying objects, environment, and sys.path.""" - - def setattr(self, target, name, value=, raising: bool = True) -> None: - """Set attribute on target object.""" - - def setitem(self, dic, name, value) -> None: - """Set dictionary item.""" - - def setenv(self, name: str, value: str, prepend: str | None = None) -> None: - """Set environment variable.""" - - def syspath_prepend(self, path) -> None: - """Prepend to sys.path.""" - - def chdir(self, path) -> None: - """Change current working directory.""" - - def undo(self) -> None: - """Undo all changes.""" -``` - -**Usage Example:** - -```python -def test_monkeypatch(monkeypatch): - # Modify object attribute - monkeypatch.setattr("os.getcwd", lambda: "/fake/path") - - # Set environment variable - monkeypatch.setenv("TEST_MODE", "true") - - # Modify dictionary - import sys - monkeypatch.setitem(sys.modules, "fake_module", FakeModule()) - - # Change directory - monkeypatch.chdir("/tmp") - - # All changes are automatically undone after test -``` - -### Output Capture - -Capture stdout, stderr, and file descriptor output during test execution. - -```python { .api } -class CaptureFixture: - """Captures stdout/stderr output during tests.""" - - def readouterr(self) -> CaptureResult: - """Read and return captured output.""" - - def disabled(self): - """Context manager to temporarily disable capturing.""" - -class CaptureResult: - """Result of captured output.""" - out: str # Captured stdout - err: str # Captured stderr -``` - -**Usage Example:** - -```python -def test_output_capture(capsys): - print("Hello stdout") - print("Hello stderr", file=sys.stderr) - - captured = capsys.readouterr() - assert captured.out == "Hello stdout\\n" - assert captured.err == "Hello stderr\\n" - - # Temporarily disable capture - with capsys.disabled(): - print("This will be printed normally") - -# Different capture fixtures available: -def test_capture_variants(capsys, capsysbinary, capfd, capfdbinary): - # capsys: capture sys.stdout/stderr (text) - # capsysbinary: capture sys.stdout/stderr (binary) - # capfd: capture file descriptors 1/2 (text) - # capfdbinary: capture file descriptors 1/2 (binary) - pass -``` - -### Log Capture - -Capture and inspect log messages during test execution. - -```python { .api } -class LogCaptureFixture: - """Provides access and control of log capturing.""" - - # Attributes - handler: LogCaptureHandler # Log handler - records: list[logging.LogRecord] # Captured log records - - def get_records(self, when: str) -> list[logging.LogRecord]: - """Get log records for specific test phase.""" - - def clear(self) -> None: - """Clear captured records.""" - - def set_level(self, level: int | str, logger: str | None = None) -> None: - """Set logging level.""" -``` - -**Usage Example:** - -```python -import logging - -def test_logging(caplog): - with caplog.at_level(logging.INFO): - logging.info("This is an info message") - logging.warning("This is a warning") - - assert len(caplog.records) == 2 - assert caplog.records[0].levelname == "INFO" - assert "info message" in caplog.records[0].message - - # Clear records - caplog.clear() - assert len(caplog.records) == 0 - - # Check specific logger - logger = logging.getLogger("myapp") - logger.error("Application error") - - assert any("Application error" in record.message - for record in caplog.records) -``` - -### Temporary Paths - -Factory for creating temporary directories and files. - -```python { .api } -class TempPathFactory: - """Factory for creating temporary directories.""" - - def mktemp(self, basename: str, numbered: bool = True) -> Path: - """Create temporary directory.""" - - def getbasetemp(self) -> Path: - """Get base temporary directory.""" - -# Built-in fixtures -def test_temp_paths(tmp_path, tmp_path_factory): - # tmp_path: pathlib.Path to temporary directory (function scope) - # tmp_path_factory: TempPathFactory instance - - # Create file in temporary directory - file_path = tmp_path / "test_file.txt" - file_path.write_text("test content") - assert file_path.read_text() == "test content" - - # Create additional temp directory - another_temp = tmp_path_factory.mktemp("custom_dir") - assert another_temp.exists() -``` - -### Plugin Testing Framework - -Comprehensive tools for testing pytest plugins and functionality. - -```python { .api } -class Pytester: - """Facilities for testing pytest plugins and functionality.""" - - def makepyfile(self, **kwargs) -> Path: - """Create Python file with content.""" - - def makeconftest(self, source: str) -> Path: - """Create conftest.py file.""" - - def makefile(self, ext: str, **kwargs) -> Path: - """Create file with given extension.""" - - def mkdir(self, name: str) -> Path: - """Create directory.""" - - def runpytest(self, *args, **kwargs) -> RunResult: - """Run pytest in subprocess.""" - - def runpytest_subprocess(self, *args, **kwargs) -> RunResult: - """Run pytest in subprocess with isolation.""" - - def runpytest_inprocess(self, *args, **kwargs) -> RunResult: - """Run pytest in same process.""" - -class RunResult: - """Result of running pytest in subprocess.""" - - # Attributes - ret: int # Return code - outlines: list[str] # stdout lines - errlines: list[str] # stderr lines - stdout: str # Full stdout - stderr: str # Full stderr - duration: float # Execution duration - - def parseoutcomes(self) -> dict[str, int]: - """Parse test outcomes from output.""" - - def assert_outcomes(self, **expected) -> None: - \"\"\"Assert expected test outcomes.\"\"\"\n\nclass HookRecorder:\n \"\"\"Records hook calls for testing.\"\"\"\n \n def getcalls(self, names: str | list[str]) -> list[RecordedHookCall]:\n \"\"\"Get recorded calls for hook names.\"\"\"\n \n def assert_contains(self, entries) -> None:\n \"\"\"Assert recorder contains specific entries.\"\"\"\n \n def pop(self, name: str) -> RecordedHookCall:\n \"\"\"Remove and return last call for hook name.\"\"\"\n\nclass RecordedHookCall:\n \"\"\"Represents a recorded hook call.\"\"\"\n pass\n\nclass LineMatcher:\n \"\"\"Flexible matching of text output lines.\"\"\"\n \n def fnmatch_lines(self, lines2: list[str]) -> None:\n \"\"\"Assert lines match using fnmatch patterns.\"\"\"\n \n def re_match_lines(self, lines2: list[str]) -> None:\n \"\"\"Assert lines match using regex patterns.\"\"\"\n \n def no_fnmatch_line(self, pat: str) -> None:\n \"\"\"Assert no line matches fnmatch pattern.\"\"\"\n```\n\n**Usage Example:**\n\n```python\ndef test_my_plugin(pytester):\n # Create test files\n pytester.makepyfile(\"\"\"\n def test_example():\n assert True\n \"\"\")\n \n pytester.makeconftest(\"\"\"\n import pytest\n \n @pytest.fixture\n def my_fixture():\n return \"test_value\"\n \"\"\")\n \n # Run pytest\n result = pytester.runpytest(\"-v\")\n \n # Check results\n result.assert_outcomes(passed=1)\n assert result.ret == 0\n \n # Check output\n result.stdout.fnmatch_lines([\n \"*test_example PASSED*\"\n ])\n\ndef test_hook_recording(pytester):\n pytester.makepyfile(\"\"\"\n def test_foo(): pass\n \"\"\")\n \n # Record hooks\n rec = pytester.runpytest(\"--collect-only\")\n \n # Check hook calls\n calls = rec.getcalls(\"pytest_collection_modifyitems\")\n assert len(calls) == 1\n```\n\n### Legacy Path Support (Deprecated)\n\nLegacy support for py.path and testdir (deprecated in favor of pathlib.Path).\n\n```python { .api }\nclass TempdirFactory:\n \"\"\"Legacy factory for temporary directories (deprecated).\"\"\"\n pass\n\nclass Testdir:\n \"\"\"Legacy test directory helper (deprecated).\"\"\"\n pass\n```\n\n## Built-in Utility Fixtures\n\nSummary of built-in fixtures for test utilities:\n\n```python\ndef test_with_all_utilities(\n monkeypatch, # MonkeyPatch instance\n capsys, # Capture sys.stdout/stderr (text)\n capsysbinary, # Capture sys.stdout/stderr (binary)\n capfd, # Capture file descriptors (text)\n capfdbinary, # Capture file descriptors (binary) \n caplog, # LogCaptureFixture instance\n tmp_path, # pathlib.Path to temp directory\n tmp_path_factory, # TempPathFactory instance\n pytester, # Pytester instance (for plugin testing)\n recwarn, # WarningsRecorder instance\n # Legacy (deprecated)\n tmpdir, # py.path.local temp directory\n tmpdir_factory, # TempdirFactory instance\n testdir # Testdir instance\n):\n pass\n``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md b/.tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md deleted file mode 100644 index e46dfbe05..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/testing-functions.md +++ /dev/null @@ -1,189 +0,0 @@ -# Testing Functions - -Core functions for controlling test execution flow and outcomes. These functions provide the fundamental building blocks for managing test behavior, conditional execution, and early termination scenarios. - -## Capabilities - -### Test Skipping - -Skip test execution with optional reason and support for module-level skipping. - -```python { .api } -def skip(reason: str = "", *, allow_module_level: bool = False): - """ - Imperatively skip a test with optional reason. - - Parameters: - - reason: Explanation for why the test is skipped - - allow_module_level: Whether to allow skipping at module level (outside test functions) - - Raises: - pytest.skip.Exception (Skipped) - """ -``` - -**Usage Example:** - -```python -import pytest - -def test_conditional_skip(): - if not has_network(): - pytest.skip("Network not available") - # test code here - -# Module-level skip -if not sys.platform.startswith("linux"): - pytest.skip("Linux-only tests", allow_module_level=True) -``` - -### Expected Failures - -Mark tests as expected to fail with optional reason. - -```python { .api } -def xfail(reason: str = ""): - """ - Mark test as expected to fail. - - Parameters: - - reason: Explanation for why the test is expected to fail - - Raises: - pytest.xfail.Exception (XFailed) - """ -``` - -**Usage Example:** - -```python -import pytest - -def test_known_bug(): - if bug_exists(): - pytest.xfail("Bug #123 not fixed yet") - assert complex_calculation() == expected_result -``` - -### Test Failures - -Explicitly fail tests with custom messages and traceback control. - -```python { .api } -def fail(reason: str = "", pytrace: bool = True): - """ - Explicitly fail a test with message and traceback control. - - Parameters: - - reason: Failure message to display - - pytrace: Whether to show pytest traceback (True) or just the message (False) - - Raises: - pytest.fail.Exception (Failed) - """ -``` - -**Usage Example:** - -```python -import pytest - -def test_validation(): - result = validate_data(data) - if not result.is_valid: - pytest.fail(f"Validation failed: {result.errors}") -``` - -### Test Suite Exit - -Exit the entire test suite immediately with reason and exit code. - -```python { .api } -def exit(reason: str = "", returncode: int | None = None): - """ - Exit testing process immediately. - - Parameters: - - reason: Explanation for early exit - - returncode: Exit code to use (defaults to appropriate pytest exit code) - - Raises: - pytest.exit.Exception (Exit) - """ -``` - -**Usage Example:** - -```python -import pytest - -def test_critical_setup(): - if not critical_system_available(): - pytest.exit("Critical system unavailable, cannot continue testing") -``` - -### Conditional Imports - -Import modules or skip tests if import fails, with version checking support. - -```python { .api } -def importorskip( - modname: str, - minversion: str | None = None, - reason: str | None = None, - *, - exc_type: type[ImportError] | None = None -): - """ - Import module or skip test if import fails. - - Parameters: - - modname: Name of module to import - - minversion: Minimum required version (if module has __version__) - - reason: Custom skip reason if import/version check fails - - exc_type: Specific exception type to catch (defaults to ImportError) - - Returns: - The imported module - - Raises: - pytest.skip.Exception if import fails or version insufficient - """ -``` - -**Usage Example:** - -```python -import pytest - -# Skip test if numpy not available -numpy = pytest.importorskip("numpy") - -# Skip if version too old -requests = pytest.importorskip("requests", minversion="2.20.0") - -def test_with_numpy(): - array = numpy.array([1, 2, 3]) - assert len(array) == 3 - -def test_with_requests(): - response = requests.get("https://httpbin.org/get") - assert response.status_code == 200 -``` - -## Exception Types - -```python { .api } -# These are the exceptions raised by the testing functions -class skip.Exception(Exception): - """Raised by pytest.skip()""" - -class xfail.Exception(Exception): - """Raised by pytest.xfail()""" - -class fail.Exception(Exception): - """Raised by pytest.fail()""" - -class exit.Exception(Exception): - """Raised by pytest.exit()""" -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/docs/warnings.md b/.tessl/tiles/tessl/pypi-pytest/docs/warnings.md deleted file mode 100644 index f6d3236f2..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/docs/warnings.md +++ /dev/null @@ -1,341 +0,0 @@ -# Warning Types and Error Handling - -Comprehensive warning type hierarchy for pytest-specific warnings including deprecation warnings, configuration warnings, collection warnings, and error handling patterns. These warnings help developers identify potential issues and deprecated functionality. - -## Capabilities - -### Base Warning Types - -Foundation warning classes for the pytest warning hierarchy. - -```python { .api } -class PytestWarning(UserWarning): - """ - Base class for all pytest warnings. - - This is the base class that all pytest-specific warnings inherit from. - It extends Python's UserWarning to provide pytest-specific warning behavior. - """ - -class PytestDeprecationWarning(PytestWarning, DeprecationWarning): - """ - Warning for features that will be removed in future versions. - - Used to warn about deprecated pytest functionality that will be removed - in upcoming major releases. Extends both PytestWarning and DeprecationWarning. - """ -``` - -### Specific Warning Categories - -Detailed warning types for different aspects of pytest functionality. - -```python { .api } -class PytestAssertRewriteWarning(PytestWarning): - """ - Warnings from the assert rewrite module. - - Issued when there are problems with assertion rewriting, such as: - - Files that cannot be rewritten - - Import-time assertion rewrite issues - - Conflicting assertion rewrite configurations - """ - -class PytestCacheWarning(PytestWarning): - """ - Cache plugin warnings. - - Issued when there are problems with pytest's caching functionality: - - Cache directory access issues - - Cache corruption problems - - Cache configuration warnings - """ - -class PytestCollectionWarning(PytestWarning): - """ - Collection failures for files or symbols. - - Issued during test collection when: - - Files cannot be imported - - Test functions/classes cannot be collected - - Collection configuration issues arise - """ - -class PytestConfigWarning(PytestWarning): - """ - Configuration issues and warnings. - - Issued when there are problems with pytest configuration: - - Invalid configuration values - - Conflicting configuration options - - Missing required configuration - """ - -class PytestUnknownMarkWarning(PytestWarning): - """ - Unknown marker usage warnings. - - Issued when tests use marks that haven't been registered: - - Unregistered custom marks - - Typos in mark names - - Missing mark definitions - """ -``` - -### Experimental and Future Warnings - -Warnings for experimental features and future changes. - -```python { .api } -class PytestExperimentalApiWarning(PytestWarning, FutureWarning): - """ - Experimental API usage warnings. - - Issued when using experimental pytest APIs that: - - May change without notice - - Are not covered by backward compatibility guarantees - - Are in development and may be removed - """ - -class PytestRemovedIn9Warning(PytestDeprecationWarning): - """ - Features that will be removed in pytest 9. - - Specific deprecation warning for functionality scheduled for removal - in pytest version 9.0. Provides clear migration path and timeline. - """ -``` - -### Runtime and Execution Warnings - -Warnings related to test execution and runtime behavior. - -```python { .api } -class PytestReturnNotNoneWarning(PytestWarning): - """ - Test functions returning non-None values. - - Issued when test functions return values other than None: - - Test functions that return values - - Generator functions used as tests incorrectly - - Functions that should be fixtures but aren't marked - """ - -class PytestUnraisableExceptionWarning(PytestWarning): - """ - Unraisable exceptions (e.g., in __del__ methods). - - Issued when exceptions occur that cannot be properly raised: - - Exceptions in __del__ methods - - Exceptions in cleanup code - - Exceptions in background threads - """ - -class PytestUnhandledThreadExceptionWarning(PytestWarning): - """ - Unhandled exceptions in threads. - - Issued when exceptions occur in threads that are not properly handled: - - Background thread exceptions - - Thread pool exceptions - - Daemon thread exceptions - """ - -class PytestFDWarning(PytestWarning): - """ - File descriptor leak warnings. - - Issued by the lsof plugin when file descriptor leaks are detected: - - Open files not properly closed - - Network connections left open - - Resource leaks between tests - """ -``` - -## Warning Usage Examples - -### Filtering Warnings - -```python -import pytest -import warnings - -# Filter warnings in test code -def test_with_warning_filter(): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", PytestDeprecationWarning) - # Code that would generate deprecation warnings - deprecated_function() - -# Using pytest.mark.filterwarnings -@pytest.mark.filterwarnings("ignore::pytest.PytestDeprecationWarning") -def test_ignore_deprecation(): - deprecated_function() - -@pytest.mark.filterwarnings("error::pytest.PytestUnknownMarkWarning") -def test_treat_unknown_marks_as_errors(): - pass -``` - -### Configuration-based Warning Control - -```python -# pytest.ini -[tool:pytest] -filterwarnings = - error - ignore::pytest.PytestDeprecationWarning - ignore::DeprecationWarning:django.* - default::pytest.PytestExperimentalApiWarning - -# pyproject.toml -[tool.pytest.ini_options] -filterwarnings = [ - "error", - "ignore::pytest.PytestDeprecationWarning", - "default::pytest.PytestExperimentalApiWarning", -] -``` - -### Custom Warning Handling - -```python -import pytest -import warnings - -def test_custom_warning_handling(recwarn): - # Trigger some pytest warnings - warnings.warn("Test warning", PytestConfigWarning) - warnings.warn("Another warning", PytestCollectionWarning) - - # Check that warnings were issued - assert len(recwarn) == 2 - assert recwarn[0].category == PytestConfigWarning - assert recwarn[1].category == PytestCollectionWarning - - # Check warning messages - assert "Test warning" in str(recwarn[0].message) - assert "Another warning" in str(recwarn[1].message) - -def test_specific_warning_check(): - with pytest.warns(PytestDeprecationWarning, match="deprecated"): - warnings.warn("This feature is deprecated", PytestDeprecationWarning) -``` - -## Common Warning Scenarios - -### Unknown Marks - -```python -# This will generate PytestUnknownMarkWarning -@pytest.mark.unknownmark # Typo or unregistered mark -def test_with_unknown_mark(): - pass - -# Fix by registering the mark -# pytest.ini -[tool:pytest] -markers = - unknownmark: description of the mark -``` - -### Return Values in Tests - -```python -# This will generate PytestReturnNotNoneWarning -def test_returning_value(): - return "Should not return anything" # Warning issued - -# Fix by not returning values -def test_fixed(): - result = calculate_something() - assert result == expected # Don't return the result -``` - -### Deprecated Features - -```python -# Using deprecated pytest features -def test_with_deprecated_feature(): - # This might generate PytestDeprecationWarning or PytestRemovedIn9Warning - pytest.deprecated_function() # Replace with newer API - -# Check for specific deprecation warnings -def test_handle_deprecation(): - with pytest.warns(PytestRemovedIn9Warning): - use_deprecated_feature() -``` - -### Configuration Issues - -```python -# pytest.ini with conflicting options might generate PytestConfigWarning -[tool:pytest] -addopts = --strict-markers -markers = - # Missing marker definitions can cause warnings - -# Fix by properly defining all markers -markers = - slow: marks tests as slow - integration: marks tests as integration tests -``` - -## Warning Hierarchy - -The complete warning hierarchy: - -``` -UserWarning -├── PytestWarning (base for all pytest warnings) - ├── PytestAssertRewriteWarning - ├── PytestCacheWarning - ├── PytestCollectionWarning - ├── PytestConfigWarning - ├── PytestUnknownMarkWarning - ├── PytestReturnNotNoneWarning - ├── PytestUnraisableExceptionWarning - ├── PytestUnhandledThreadExceptionWarning - ├── PytestFDWarning - ├── PytestDeprecationWarning (also extends DeprecationWarning) - │ └── PytestRemovedIn9Warning - └── PytestExperimentalApiWarning (also extends FutureWarning) -``` - -## Best Practices - -### Handling Warnings in Tests - -```python -# Don't ignore all warnings - be specific -@pytest.mark.filterwarnings("ignore::DeprecationWarning:specific_module.*") - -# Test that warnings are properly issued -def test_warning_issued(): - with pytest.warns(PytestDeprecationWarning): - deprecated_api() - -# Handle warnings in fixtures -@pytest.fixture -def ignore_known_warnings(): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", PytestConfigWarning) - yield -``` - -### Warning Documentation - -```python -def deprecated_function(): - """ - This function is deprecated. - - .. deprecated:: 8.0 - Use new_function() instead. Will be removed in pytest 9.0. - """ - warnings.warn( - "deprecated_function is deprecated, use new_function instead", - PytestRemovedIn9Warning, - stacklevel=2 - ) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-pytest/tile.json b/.tessl/tiles/tessl/pypi-pytest/tile.json deleted file mode 100644 index fe31228f4..000000000 --- a/.tessl/tiles/tessl/pypi-pytest/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-pytest", - "version": "8.4.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/pytest@8.4.2", - "summary": "pytest: simple powerful testing with Python" -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md deleted file mode 100644 index c565e37aa..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/docs/cli.md +++ /dev/null @@ -1,288 +0,0 @@ -# Command Line Interface - -Complete command-line tool for managing .env files with list, get, set, unset, and run commands supporting multiple output formats. - -## Installation - -```bash -pip install "python-dotenv[cli]" -``` - -## Global Options - -All commands support these global options: - -```bash { .api } -dotenv [OPTIONS] COMMAND [ARGS]... - -Options: - -f, --file PATH Location of the .env file, defaults to .env file in current working directory - -q, --quote [always|never|auto] Whether to quote or not the variable values. Default mode is always - -e, --export Whether to write the dot file as an executable bash script - --version Show the version and exit - --help Show help message and exit -``` - -## Capabilities - -### Listing Variables - -Display all stored key/value pairs with support for multiple output formats. - -```bash { .api } -dotenv list [OPTIONS] - -Options: - --format [simple|json|shell|export] The format in which to display the list (default: simple) -``` - -Usage examples: - -```bash -# List all variables in simple format -dotenv list - -# List in JSON format -dotenv list --format=json - -# List in shell format (with proper quoting) -dotenv list --format=shell - -# List in export format (prefixed with 'export') -dotenv list --format=export - -# List from custom file -dotenv -f /path/to/custom.env list -``` - -Output examples: - -```bash -# Simple format -DATABASE_URL=postgresql://localhost/mydb -DEBUG=True -API_KEY=secret123 - -# JSON format -{ - "API_KEY": "secret123", - "DATABASE_URL": "postgresql://localhost/mydb", - "DEBUG": "True" -} - -# Shell format (properly quoted) -DATABASE_URL='postgresql://localhost/mydb' -DEBUG=True -API_KEY=secret123 - -# Export format -export DATABASE_URL='postgresql://localhost/mydb' -export DEBUG=True -export API_KEY=secret123 -``` - -### Getting Values - -Retrieve the value for a specific key. - -```bash { .api } -dotenv get KEY -``` - -Usage examples: - -```bash -# Get a specific value -dotenv get DATABASE_URL - -# Get from custom file -dotenv -f production.env get API_KEY - -# Handle missing keys (exits with code 1) -dotenv get NONEXISTENT_KEY -echo $? # Returns 1 -``` - -### Setting Values - -Store key/value pairs in the .env file. - -```bash { .api } -dotenv set KEY VALUE -``` - -Usage examples: - -```bash -# Set a basic key/value pair -dotenv set DATABASE_URL "postgresql://localhost/mydb" - -# Set with different quote modes -dotenv -q never set SIMPLE_KEY simple_value -dotenv -q auto set SPACED_KEY "value with spaces" -dotenv -q always set QUOTED_KEY always_quoted - -# Set with export prefix -dotenv -e set PATH_ADDITION "/usr/local/bin" - -# Set in custom file -dotenv -f production.env set API_KEY "prod-secret-123" - -# Set complex values -dotenv set JSON_CONFIG '{"host": "localhost", "port": 5432}' -dotenv set MULTILINE_VALUE "line1 -line2 -line3" -``` - -### Removing Values - -Remove keys from the .env file. - -```bash { .api } -dotenv unset KEY -``` - -Usage examples: - -```bash -# Remove a key -dotenv unset OLD_CONFIG - -# Remove from custom file -dotenv -f staging.env unset TEMP_KEY - -# Handle missing keys (exits with code 1) -dotenv unset NONEXISTENT_KEY -echo $? # Returns 1 -``` - -### Running Commands - -Execute commands with environment variables loaded from the .env file. - -```bash { .api } -dotenv run [OPTIONS] COMMAND [ARGS]... - -Options: - --override/--no-override Override variables from the environment file with those from the .env file (default: --override) -``` - -Usage examples: - -```bash -# Run a Python script with .env variables -dotenv run python app.py - -# Run with arguments -dotenv run python manage.py migrate - -# Run from custom .env file -dotenv -f production.env run python app.py - -# Don't override existing environment variables -dotenv run --no-override python app.py - -# Run complex commands -dotenv run sh -c 'echo "Database: $DATABASE_URL"' - -# Run commands that need specific environment -dotenv run pytest tests/ -dotenv run npm start -dotenv run make build -``` - -The `run` command: -- Loads variables from the .env file -- Merges them with existing environment variables -- Executes the specified command with the combined environment -- **With `--override` (default)**: .env file variables take precedence over existing environment variables -- **With `--no-override`**: existing environment variables take precedence over .env file variables -- Variables that exist only in .env file or only in environment are always included - -## File Selection - -Use the `-f/--file` option to specify a custom .env file: - -```bash -# Different .env files for different environments -dotenv -f .env.development list -dotenv -f .env.staging set API_URL "https://staging-api.example.com" -dotenv -f .env.production run python deploy.py - -# Absolute paths -dotenv -f /etc/myapp/.env list - -# Relative paths -dotenv -f ../shared.env get SHARED_KEY -``` - -## Quote Modes - -Control how values are quoted using the `-q/--quote` option: - -```bash -# Always quote (default) -dotenv -q always set KEY "value" # KEY='value' - -# Never quote -dotenv -q never set KEY value # KEY=value - -# Auto quote (quotes only when necessary) -dotenv -q auto set SIMPLE abc # SIMPLE=abc -dotenv -q auto set COMPLEX "a b" # COMPLEX='a b' -``` - -## Export Mode - -Generate bash-compatible output using the `-e/--export` option: - -```bash -# Set with export prefix -dotenv -e set PATH_VAR "/usr/local/bin" -# Results in: export PATH_VAR='/usr/local/bin' - -# List with export format -dotenv -e list --format=export -# Shows all variables prefixed with 'export' - -# Create sourceable .env file -dotenv -e -f bash-compatible.env set DATABASE_URL "postgresql://localhost/mydb" -# Now you can: source bash-compatible.env -``` - -## Exit Codes - -The CLI tool uses standard exit codes: - -- **0**: Success -- **1**: Command failed (key not found, file doesn't exist, etc.) -- **2**: File access error (permissions, file not readable, etc.) - -## Examples - -```bash -# Complete workflow example -cd myproject - -# Initialize with some variables -dotenv set DATABASE_URL "postgresql://localhost/mydb" -dotenv set DEBUG "True" -dotenv set SECRET_KEY "dev-secret-key" - -# List all variables -dotenv list - -# Get specific value -echo "Database: $(dotenv get DATABASE_URL)" - -# Run application with environment -dotenv run python app.py - -# Export for bash sourcing -dotenv -e list --format=export > environment.sh -source environment.sh - -# Clean up -dotenv unset SECRET_KEY -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md deleted file mode 100644 index 6aa3599d4..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/docs/environment-loading.md +++ /dev/null @@ -1,202 +0,0 @@ -# Environment Loading - -Core functionality for loading environment variables from .env files with automatic file discovery, variable expansion, and flexible configuration options. - -## Capabilities - -### Loading Environment Variables - -Load environment variables from .env files directly into the system environment, with support for override control and variable interpolation. - -```python { .api } -def load_dotenv( - dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, - stream: Optional[IO[str]] = None, - verbose: bool = False, - override: bool = False, - interpolate: bool = True, - encoding: Optional[str] = "utf-8" -) -> bool: - """ - Parse a .env file and then load all the variables found as environment variables. - - Parameters: - dotenv_path: Absolute or relative path to .env file - stream: Text stream (such as io.StringIO) with .env content, used if dotenv_path is None - verbose: Whether to output a warning if the .env file is missing - override: Whether to override the system environment variables with variables from the .env file - interpolate: Whether to interpolate variables using POSIX variable expansion - encoding: Encoding to be used to read the file - - Returns: - bool: True if at least one environment variable is set, else False - """ -``` - -Usage examples: - -```python -from dotenv import load_dotenv -import os - -# Load from default .env file -load_dotenv() - -# Load from specific file -load_dotenv('/path/to/custom.env') - -# Override existing environment variables -load_dotenv(override=True) - -# Load from string stream -from io import StringIO -config = StringIO("DATABASE_URL=sqlite:///app.db\nDEBUG=True") -load_dotenv(stream=config) - -# Disable variable interpolation -load_dotenv(interpolate=False) -``` - -### Parsing Without Environment Modification - -Parse .env files and return their contents as a dictionary without modifying the system environment. - -```python { .api } -def dotenv_values( - dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, - stream: Optional[IO[str]] = None, - verbose: bool = False, - interpolate: bool = True, - encoding: Optional[str] = "utf-8" -) -> Dict[str, Optional[str]]: - """ - Parse a .env file and return its content as a dict. - - The returned dict will have None values for keys without values in the .env file. - For example, 'foo=bar' results in {"foo": "bar"} whereas 'foo' alone results in {"foo": None} - - Parameters: - dotenv_path: Absolute or relative path to the .env file - stream: StringIO object with .env content, used if dotenv_path is None - verbose: Whether to output a warning if the .env file is missing - interpolate: Whether to interpolate variables using POSIX variable expansion - encoding: Encoding to be used to read the file - - Returns: - dict: Dictionary with keys and values from the .env file - """ -``` - -Usage examples: - -```python -from dotenv import dotenv_values -import os - -# Parse .env file to dictionary -config = dotenv_values(".env") -database_url = config.get('DATABASE_URL') - -# Advanced configuration management -config = { - **dotenv_values(".env.shared"), # load shared development variables - **dotenv_values(".env.secret"), # load sensitive variables - **os.environ, # override loaded values with environment variables -} - -# Parse from stream -from io import StringIO -env_stream = StringIO("USER=foo\nEMAIL=foo@example.org") -config = dotenv_values(stream=env_stream) -# Returns: {"USER": "foo", "EMAIL": "foo@example.org"} -``` - -### File Discovery - -Automatically discover .env files by searching in increasingly higher folders from the current directory or script location. - -```python { .api } -def find_dotenv( - filename: str = ".env", - raise_error_if_not_found: bool = False, - usecwd: bool = False -) -> str: - """ - Search in increasingly higher folders for the given file. - - Parameters: - filename: Name of the file to search for (default: ".env") - raise_error_if_not_found: Whether to raise IOError if file is not found - usecwd: Whether to start search from current working directory instead of script location - - Returns: - str: Path to the file if found, or an empty string otherwise - - Raises: - IOError: If raise_error_if_not_found is True and file is not found - """ -``` - -Usage examples: - -```python -from dotenv import find_dotenv, load_dotenv - -# Find .env file automatically -env_path = find_dotenv() -if env_path: - load_dotenv(env_path) - -# Search for custom filename -config_path = find_dotenv('.config') - -# Raise error if not found -try: - env_path = find_dotenv(raise_error_if_not_found=True) - load_dotenv(env_path) -except IOError: - print("No .env file found") - -# Search from current working directory -env_path = find_dotenv(usecwd=True) -``` - -## Variable Expansion - -python-dotenv supports POSIX-style variable expansion using `${VAR}` syntax with optional default values: - -```bash -# .env file example -DOMAIN=example.org -ADMIN_EMAIL=admin@${DOMAIN} -ROOT_URL=${DOMAIN}/app -API_KEY=${SECRET_KEY:-default-key} - -# More complex examples -DATABASE_HOST=localhost -DATABASE_PORT=5432 -DATABASE_NAME=myapp -DATABASE_URL=postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME} - -# Using defaults for optional configuration -REDIS_URL=${REDIS_HOST:-localhost}:${REDIS_PORT:-6379} -LOG_LEVEL=${DEBUG:-false} -MAX_WORKERS=${WORKER_COUNT:-4} - -# Nested variable expansion -APP_ENV=production -CONFIG_FILE=config.${APP_ENV}.json -CONFIG_PATH=/etc/myapp/${CONFIG_FILE} -``` - -With `load_dotenv(override=True)` or `dotenv_values()`, variable resolution follows this priority: -1. Value of that variable in the .env file -2. Value of that variable in the environment -3. Default value (if provided with `:-` syntax) -4. Empty string - -With `load_dotenv(override=False)`, the priority is: -1. Value of that variable in the environment -2. Value of that variable in the .env file -3. Default value (if provided with `:-` syntax) -4. Empty string \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md deleted file mode 100644 index a677e444d..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/docs/file-manipulation.md +++ /dev/null @@ -1,232 +0,0 @@ -# File Manipulation - -Programmatic functions for reading, writing, and modifying .env files with support for different quote modes and export formats. - -## Capabilities - -### Reading Values - -Retrieve the value of a specific key from a .env file without loading all variables. - -```python { .api } -def get_key( - dotenv_path: Union[str, os.PathLike[str]], - key_to_get: str, - encoding: Optional[str] = "utf-8" -) -> Optional[str]: - """ - Get the value of a given key from the given .env file. - - Parameters: - dotenv_path: Path to the .env file - key_to_get: The key to retrieve - encoding: File encoding (default: "utf-8") - - Returns: - str or None: Value of the key if found, None if key doesn't exist or has no value - """ -``` - -Usage examples: - -```python -from dotenv import get_key - -# Get a specific value -database_url = get_key('.env', 'DATABASE_URL') -if database_url: - print(f"Database URL: {database_url}") - -# Handle missing keys -api_key = get_key('.env', 'API_KEY') -if api_key is None: - print("API_KEY not found in .env file") -``` - -### Setting Values - -Add or update key/value pairs in .env files with support for different quoting modes and export formats. - -```python { .api } -def set_key( - dotenv_path: Union[str, os.PathLike[str]], - key_to_set: str, - value_to_set: str, - quote_mode: str = "always", - export: bool = False, - encoding: Optional[str] = "utf-8" -) -> Tuple[Optional[bool], str, str]: - """ - Add or update a key/value to the given .env file. - - If the .env path given doesn't exist, fails instead of risking creating - an orphan .env somewhere in the filesystem. - - Parameters: - dotenv_path: Path to the .env file - key_to_set: The key to set - value_to_set: The value to set - quote_mode: Quote mode - "always", "auto", or "never" - export: Whether to prefix the line with "export " - encoding: File encoding (default: "utf-8") - - Returns: - tuple: (success_bool, key, value) where success_bool indicates if operation succeeded - - Raises: - ValueError: If quote_mode is not one of "always", "auto", "never" - """ -``` - -Usage examples: - -```python -from dotenv import set_key - -# Set a basic key/value pair -success, key, value = set_key('.env', 'DATABASE_URL', 'postgresql://localhost/mydb') -if success: - print(f"Set {key}={value}") - -# Set with different quote modes -set_key('.env', 'SIMPLE_KEY', 'simple_value', quote_mode='never') # SIMPLE_KEY=simple_value -set_key('.env', 'SPACED_KEY', 'value with spaces', quote_mode='auto') # SPACED_KEY='value with spaces' -set_key('.env', 'QUOTED_KEY', 'always_quoted', quote_mode='always') # QUOTED_KEY='always_quoted' - -# Set with export prefix for bash compatibility -set_key('.env', 'PATH_VAR', '/usr/local/bin', export=True) -# Results in: export PATH_VAR='/usr/local/bin' - -# Handle special characters -set_key('.env', 'PASSWORD', "p@ssw0rd'with\"quotes", quote_mode='always') -``` - -### Removing Values - -Remove keys and their values from .env files. - -```python { .api } -def unset_key( - dotenv_path: Union[str, os.PathLike[str]], - key_to_unset: str, - quote_mode: str = "always", - encoding: Optional[str] = "utf-8" -) -> Tuple[Optional[bool], str]: - """ - Remove a given key from the given .env file. - - Parameters: - dotenv_path: Path to the .env file - key_to_unset: The key to remove - quote_mode: Quote mode for file operations (currently unused but kept for consistency) - encoding: File encoding (default: "utf-8") - - Returns: - tuple: (success_bool, key) where success_bool is True if key was removed, - None if file doesn't exist or key wasn't found - - Raises: - Warning logged if file doesn't exist or key doesn't exist - """ -``` - -Usage examples: - -```python -from dotenv import unset_key - -# Remove a key -success, key = unset_key('.env', 'OLD_CONFIG_KEY') -if success: - print(f"Removed {key} from .env file") -elif success is None: - print("Key not found or file doesn't exist") - -# Remove multiple keys -keys_to_remove = ['TEMP_KEY', 'DEPRECATED_SETTING', 'OLD_API_KEY'] -for key in keys_to_remove: - success, removed_key = unset_key('.env', key) - if success: - print(f"Removed {removed_key}") -``` - -## Quote Modes - -The `quote_mode` parameter controls how values are quoted in the .env file: - -- **"always"** (default): All values are wrapped in single quotes -- **"auto"**: Values containing non-alphanumeric characters are quoted, simple values are not -- **"never"**: Values are never quoted (use with caution for values with spaces or special characters) - -Examples: - -```python -from dotenv import set_key - -# Different quote modes with the same value -value = "hello world" - -set_key('.env', 'KEY1', value, quote_mode='always') # KEY1='hello world' -set_key('.env', 'KEY2', value, quote_mode='auto') # KEY2='hello world' (has space) -set_key('.env', 'KEY3', value, quote_mode='never') # KEY3=hello world (may cause issues) - -# Auto mode examples -set_key('.env', 'SIMPLE', 'abc123', quote_mode='auto') # SIMPLE=abc123 (no quotes) -set_key('.env', 'COMPLEX', 'abc-123', quote_mode='auto') # COMPLEX='abc-123' (has hyphen) -``` - -## Export Mode - -When `export=True` is used with `set_key()`, the resulting line is prefixed with `export `, making the .env file compatible with bash sourcing: - -```python -from dotenv import set_key - -set_key('.env', 'PATH_ADDITION', '/usr/local/bin', export=True) -# Results in: export PATH_ADDITION='/usr/local/bin' - -# Now the .env file can be sourced in bash: -# $ source .env -# $ echo $PATH_ADDITION -``` - -## Error Handling - -The file manipulation functions handle various error conditions: - -- **File doesn't exist**: - - `get_key()`: Returns `None` and logs warning with verbose=True - - `unset_key()`: Returns `(None, key)` and logs warning - - `set_key()`: Raises `IOError` if the file path doesn't exist -- **Key doesn't exist**: - - `get_key()`: Returns `None` and logs warning with verbose=True - - `unset_key()`: Returns `(None, key)` and logs warning -- **Invalid quote mode**: `set_key()` raises `ValueError` for modes other than "always", "auto", "never" -- **File permissions**: OS-level `PermissionError` and `IOError` exceptions are propagated to caller -- **Invalid file encoding**: `UnicodeDecodeError` may be raised if encoding parameter doesn't match file - -Examples of error handling: - -```python -from dotenv import get_key, set_key, unset_key - -# Handle missing files -try: - value = get_key('nonexistent.env', 'KEY') - if value is None: - print("Key not found or file doesn't exist") -except IOError as e: - print(f"File access error: {e}") - -# Handle invalid quote modes -try: - set_key('.env', 'KEY', 'value', quote_mode='invalid') -except ValueError as e: - print(f"Invalid quote mode: {e}") - -# Handle file creation failure -try: - set_key('/readonly/path/.env', 'KEY', 'value') -except (IOError, PermissionError) as e: - print(f"Cannot write to file: {e}") -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/index.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/index.md deleted file mode 100644 index 2e16161b1..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/docs/index.md +++ /dev/null @@ -1,213 +0,0 @@ -# python-dotenv - -A Python library that reads key-value pairs from a `.env` file and can set them as environment variables. It helps in the development of applications following the 12-factor principles by providing a convenient way to manage configuration through environment variables. - -## Package Information - -- **Package Name**: python-dotenv -- **Language**: Python -- **Installation**: `pip install python-dotenv` -- **CLI Installation**: `pip install "python-dotenv[cli]"` - -## Core Imports - -```python -from dotenv import load_dotenv, dotenv_values -``` - -Complete imports for all functionality: - -```python -from dotenv import ( - load_dotenv, - dotenv_values, - find_dotenv, - get_key, - set_key, - unset_key, - get_cli_string, - load_ipython_extension -) -``` - -## Basic Usage - -```python -from dotenv import load_dotenv -import os - -# Load environment variables from .env file -load_dotenv() - -# Now you can access environment variables -database_url = os.getenv('DATABASE_URL') -debug_mode = os.getenv('DEBUG', 'False').lower() == 'true' - -# Use environment variables in your application -print(f"Database URL: {database_url}") -print(f"Debug mode: {debug_mode}") -``` - -Alternative approach without modifying environment: - -```python -from dotenv import dotenv_values - -# Parse .env file and return as dictionary -config = dotenv_values(".env") -database_url = config.get('DATABASE_URL') -debug_mode = config.get('DEBUG', 'False').lower() == 'true' -``` - -## Architecture - -python-dotenv follows a modular architecture: - -- **Loading Functions**: `load_dotenv()` and `dotenv_values()` provide the primary interfaces for loading configuration -- **File Discovery**: `find_dotenv()` automatically discovers .env files in the directory hierarchy -- **File Manipulation**: `get_key()`, `set_key()`, `unset_key()` provide programmatic access to .env file contents -- **Variable Expansion**: Built-in support for POSIX-style variable expansion using `${VAR}` syntax -- **CLI Interface**: Complete command-line tool for .env file manipulation -- **IPython Integration**: Magic commands for interactive development environments - -## Capabilities - -### Environment Loading - -Load environment variables from .env files with automatic file discovery, variable expansion, and flexible override options. - -```python { .api } -def load_dotenv( - dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, - stream: Optional[IO[str]] = None, - verbose: bool = False, - override: bool = False, - interpolate: bool = True, - encoding: Optional[str] = "utf-8" -) -> bool: ... - -def dotenv_values( - dotenv_path: Optional[Union[str, os.PathLike[str]]] = None, - stream: Optional[IO[str]] = None, - verbose: bool = False, - interpolate: bool = True, - encoding: Optional[str] = "utf-8" -) -> Dict[str, Optional[str]]: ... - -def find_dotenv( - filename: str = ".env", - raise_error_if_not_found: bool = False, - usecwd: bool = False -) -> str: ... -``` - -[Environment Loading](./environment-loading.md) - -### File Manipulation - -Programmatically read, write, and modify .env files with support for different quote modes and export formats. - -```python { .api } -def get_key( - dotenv_path: Union[str, os.PathLike[str]], - key_to_get: str, - encoding: Optional[str] = "utf-8" -) -> Optional[str]: ... - -def set_key( - dotenv_path: Union[str, os.PathLike[str]], - key_to_set: str, - value_to_set: str, - quote_mode: str = "always", - export: bool = False, - encoding: Optional[str] = "utf-8" -) -> Tuple[Optional[bool], str, str]: ... - -def unset_key( - dotenv_path: Union[str, os.PathLike[str]], - key_to_unset: str, - quote_mode: str = "always", - encoding: Optional[str] = "utf-8" -) -> Tuple[Optional[bool], str]: ... -``` - -[File Manipulation](./file-manipulation.md) - -### Command Line Interface - -Complete CLI tool for managing .env files with list, get, set, unset, and run commands supporting multiple output formats. - -```bash { .api } -dotenv list [--format=FORMAT] -dotenv get KEY -dotenv set KEY VALUE -dotenv unset KEY -dotenv run [--override/--no-override] COMMAND -``` - -[Command Line Interface](./cli.md) - -### IPython Integration - -Magic commands for loading .env files in IPython and Jupyter notebook environments with override and verbose options. - -```python { .api } -def load_ipython_extension(ipython: Any) -> None: ... - -# Magic command usage: -# %load_ext dotenv -# %dotenv [-o] [-v] [dotenv_path] -``` - -[IPython Integration](./ipython.md) - -### Utility Functions - -Helper functions for generating CLI commands and working with dotenv programmatically. - -```python { .api } -def get_cli_string( - path: Optional[str] = None, - action: Optional[str] = None, - key: Optional[str] = None, - value: Optional[str] = None, - quote: Optional[str] = None -) -> str: ... -``` - -[Utilities](./utilities.md) - -## Types - -```python { .api } -# Type aliases -StrPath = Union[str, os.PathLike[str]] - -# Core classes (internal, accessed via functions) -class DotEnv: - def __init__( - self, - dotenv_path: Optional[StrPath], - stream: Optional[IO[str]] = None, - verbose: bool = False, - encoding: Optional[str] = None, - interpolate: bool = True, - override: bool = True - ) -> None: ... - - def dict(self) -> Dict[str, Optional[str]]: ... - def parse(self) -> Iterator[Tuple[str, Optional[str]]]: ... - def set_as_environment_variables(self) -> bool: ... - def get(self, key: str) -> Optional[str]: ... - -# Parser types (internal implementation) -class Binding(NamedTuple): - key: Optional[str] - value: Optional[str] - original: "Original" - error: bool - -class Original(NamedTuple): - string: str - line: int -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md deleted file mode 100644 index b4135137e..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/docs/ipython.md +++ /dev/null @@ -1,257 +0,0 @@ -# IPython Integration - -Magic commands for loading .env files in IPython and Jupyter notebook environments with override and verbose options. - -## Installation - -The IPython extension is included with the base python-dotenv package: - -```bash -pip install python-dotenv -``` - -## Capabilities - -### Loading the Extension - -Load the dotenv extension to enable the `%dotenv` magic command. - -```python { .api } -def load_ipython_extension(ipython: Any) -> None: - """ - Register the %dotenv magic command. - - This function is called automatically when the extension is loaded. - """ -``` - -Usage in IPython/Jupyter: - -```python -# Load the extension -%load_ext dotenv - -# Extension is now available for the current session -``` - -### Magic Command - -Use the `%dotenv` magic command to load environment variables from .env files. - -```python { .api } -%dotenv [OPTIONS] [dotenv_path] - -Options: - -o, --override Indicate to override existing variables - -v, --verbose Indicate function calls to be verbose - -Arguments: - dotenv_path Search in increasingly higher folders for the dotenv_path (default: '.env') -``` - -## Usage Examples - -### Basic Usage - -```python -# Load extension -%load_ext dotenv - -# Load from default .env file -%dotenv - -# Now environment variables from .env are available -import os -database_url = os.getenv('DATABASE_URL') -print(f"Database URL: {database_url}") -``` - -### Loading from Specific Files - -```python -# Load from specific file in current directory -%dotenv config.env - -# Load from relative path -%dotenv ../shared.env - -# Load from absolute path -%dotenv /path/to/production.env -``` - -### Using Options - -```python -# Load with verbose output -%dotenv -v -# Output: Loading .env file found at: /current/directory/.env - -# Override existing environment variables -%dotenv -o - -# Combine options -%dotenv -o -v production.env -``` - -### Multiple Environment Files - -```python -# Load multiple files in sequence -%dotenv shared.env -%dotenv -o local.env # Override with local settings - -# Load different files for different purposes -%dotenv database.env # Database configuration -%dotenv api.env # API keys and endpoints -%dotenv -o debug.env # Debug settings (override existing) -``` - -## Integration with Jupyter Notebooks - -The IPython integration is particularly useful in Jupyter notebooks for data science and development workflows: - -```python -# Cell 1: Load environment configuration -%load_ext dotenv -%dotenv -v config.env - -# Cell 2: Use environment variables -import os -import pandas as pd -from sqlalchemy import create_engine - -# Get database connection from environment -database_url = os.getenv('DATABASE_URL') -engine = create_engine(database_url) - -# Load data -df = pd.read_sql('SELECT * FROM users', engine) -df.head() -``` - -```python -# Example: Different environments for different notebook sections -# Development section -%dotenv -v development.env - -# Load development data -dev_api_key = os.getenv('API_KEY') -dev_data = fetch_data(dev_api_key) - -# Production comparison section -%dotenv -o production.env - -# Load production data -prod_api_key = os.getenv('API_KEY') -prod_data = fetch_data(prod_api_key) - -# Compare datasets -compare_data(dev_data, prod_data) -``` - -## Options Details - -### Override Option (`-o, --override`) - -Controls whether environment variables from the .env file override existing environment variables: - -```python -# Without override (default behavior) -import os -os.environ['DEBUG'] = 'False' # Set in current environment -%dotenv # .env contains DEBUG=True -print(os.getenv('DEBUG')) # Prints: False (existing value preserved) - -# With override -import os -os.environ['DEBUG'] = 'False' # Set in current environment -%dotenv -o # .env contains DEBUG=True -print(os.getenv('DEBUG')) # Prints: True (overridden by .env) -``` - -### Verbose Option (`-v, --verbose`) - -Enables verbose output to show which .env file is being loaded: - -```python -# Without verbose -%dotenv config.env -# (no output) - -# With verbose -%dotenv -v config.env -# Output: Loading .env file found at: /current/directory/config.env -``` - -## Error Handling - -The magic command handles common error scenarios gracefully: - -```python -# File not found -%dotenv nonexistent.env -# Output: cannot find .env file - -# No file specified and no default .env -%dotenv -# (silently continues if no .env file found) - -# With verbose, shows search behavior -%dotenv -v -# Output: Loading .env file found at: /path/to/.env -# Or: python-dotenv could not find configuration file .env -``` - -## Automatic Extension Loading - -For convenience, you can automatically load the extension when starting IPython by adding it to your IPython configuration: - -```python -# In ~/.ipython/profile_default/ipython_config.py -c.InteractiveShellApp.extensions = ['dotenv'] - -# Or add to startup file -# In ~/.ipython/profile_default/startup/00-dotenv.py -get_ipython().magic('load_ext dotenv') -``` - -## Working with Virtual Environments - -The IPython integration works seamlessly with virtual environments: - -```python -# In a virtual environment -%load_ext dotenv -%dotenv -v - -# Variables are loaded for the current Python session -import os -print(f"Virtual env: {os.getenv('VIRTUAL_ENV')}") -print(f"App config: {os.getenv('APP_CONFIG')}") -``` - -## Best Practices - -1. **Load early**: Load your .env files early in your notebook before importing other modules -2. **Use verbose mode**: Use `-v` for debugging to see which files are being loaded -3. **Organize by purpose**: Use separate .env files for different configurations (database, API keys, debug settings) -4. **Override carefully**: Use `-o` only when you need to override existing environment variables -5. **Document in notebooks**: Add markdown cells explaining which .env files are needed - -```python -# Good practice example -%load_ext dotenv - -# Load base configuration -%dotenv -v base.env - -# Load environment-specific overrides -%dotenv -o -v development.env - -# Now proceed with analysis -import pandas as pd -import requests - -api_key = os.getenv('API_KEY') -database_url = os.getenv('DATABASE_URL') -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md b/.tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md deleted file mode 100644 index 7eb4e9b68..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/docs/utilities.md +++ /dev/null @@ -1,276 +0,0 @@ -# Utilities - -Helper functions for generating CLI commands and working with dotenv programmatically. - -## Capabilities - -### CLI Command Generation - -Generate shell command strings for dotenv operations, useful for automation and integration with other tools. - -```python { .api } -def get_cli_string( - path: Optional[str] = None, - action: Optional[str] = None, - key: Optional[str] = None, - value: Optional[str] = None, - quote: Optional[str] = None -) -> str: - """ - Return a string suitable for running as a shell script. - - Useful for converting arguments passed to a fabric task - to be passed to a `local` or `run` command. - - Parameters: - path: Path to the .env file (-f option) - action: CLI action (get, set, unset, list, run) - key: Key name for get/set/unset operations - value: Value for set operations - quote: Quote mode (-q option: always, auto, never) - - Returns: - str: Complete dotenv command string ready for shell execution - """ -``` - -## Usage Examples - -### Basic Command Generation - -```python -from dotenv import get_cli_string - -# Generate basic commands -cmd = get_cli_string(action='list') -print(cmd) # Output: dotenv list - -cmd = get_cli_string(action='get', key='DATABASE_URL') -print(cmd) # Output: dotenv get DATABASE_URL - -cmd = get_cli_string(action='set', key='DEBUG', value='True') -print(cmd) # Output: dotenv set DEBUG True -``` - -### Commands with File Paths - -```python -# Generate commands with custom file paths -cmd = get_cli_string(path='production.env', action='list') -print(cmd) # Output: dotenv -f production.env list - -cmd = get_cli_string(path='/etc/app/.env', action='get', key='API_KEY') -print(cmd) # Output: dotenv -f /etc/app/.env get API_KEY - -cmd = get_cli_string(path='config.env', action='set', key='HOST', value='localhost') -print(cmd) # Output: dotenv -f config.env set HOST localhost -``` - -### Commands with Quote Modes - -```python -# Generate commands with different quote modes -cmd = get_cli_string(action='set', key='SIMPLE', value='abc', quote='never') -print(cmd) # Output: dotenv -q never set SIMPLE abc - -cmd = get_cli_string(action='set', key='COMPLEX', value='hello world', quote='auto') -print(cmd) # Output: dotenv -q auto set COMPLEX "hello world" - -cmd = get_cli_string(action='set', key='QUOTED', value='always', quote='always') -print(cmd) # Output: dotenv -q always set QUOTED always -``` - -### Complex Command Generation - -```python -# Generate complex commands with multiple options -cmd = get_cli_string( - path='staging.env', - action='set', - key='DATABASE_URL', - value='postgresql://user:pass@localhost/db', - quote='always' -) -print(cmd) # Output: dotenv -q always -f staging.env set DATABASE_URL "postgresql://user:pass@localhost/db" -``` - -### Value Handling - -The function automatically handles values that contain spaces by wrapping them in quotes: - -```python -# Values with spaces are automatically quoted -cmd = get_cli_string(action='set', key='MESSAGE', value='hello world') -print(cmd) # Output: dotenv set MESSAGE "hello world" - -# Values without spaces remain unquoted -cmd = get_cli_string(action='set', key='PORT', value='8080') -print(cmd) # Output: dotenv set PORT 8080 - -# Empty or None values are handled gracefully -cmd = get_cli_string(action='get', key='MISSING_KEY') -print(cmd) # Output: dotenv get MISSING_KEY -``` - -## Integration Examples - -### Fabric Integration - -```python -from fabric import task -from dotenv import get_cli_string - -@task -def deploy_config(c, env='production'): - """Deploy configuration to remote server.""" - - # Generate commands for different environments - commands = [ - get_cli_string(path=f'{env}.env', action='set', key='DEPLOYED_AT', value='$(date)'), - get_cli_string(path=f'{env}.env', action='set', key='VERSION', value='1.2.3'), - get_cli_string(path=f'{env}.env', action='list', quote='export') - ] - - for cmd in commands: - c.run(cmd) -``` - -### Automation Scripts - -```python -import subprocess -from dotenv import get_cli_string - -def setup_environment(config_file, settings): - """Automate environment setup using generated commands.""" - - commands = [] - - # Generate set commands for all settings - for key, value in settings.items(): - cmd = get_cli_string( - path=config_file, - action='set', - key=key, - value=str(value), - quote='auto' - ) - commands.append(cmd) - - # Execute all commands - for cmd in commands: - print(f"Executing: {cmd}") - subprocess.run(cmd, shell=True, check=True) - - # List final configuration - list_cmd = get_cli_string(path=config_file, action='list') - subprocess.run(list_cmd, shell=True) - -# Usage -settings = { - 'DATABASE_URL': 'postgresql://localhost/myapp', - 'REDIS_URL': 'redis://localhost:6379', - 'DEBUG': 'False', - 'SECRET_KEY': 'production-secret-key' -} - -setup_environment('production.env', settings) -``` - -### Build System Integration - -```python -import os -from dotenv import get_cli_string - -def generate_build_commands(environment='development'): - """Generate build commands with environment-specific configuration.""" - - env_file = f'.env.{environment}' - - # Commands for build process - commands = [ - # Set build-specific variables - get_cli_string(path=env_file, action='set', key='NODE_ENV', value=environment), - get_cli_string(path=env_file, action='set', key='BUILD_TIME', value='$(date)'), - - # Run build with environment - get_cli_string(path=env_file, action='run') + ' npm run build', - get_cli_string(path=env_file, action='run') + ' npm run test', - ] - - return commands - -# Generate commands for different environments -dev_commands = generate_build_commands('development') -prod_commands = generate_build_commands('production') - -print("Development build commands:") -for cmd in dev_commands: - print(f" {cmd}") - -print("\nProduction build commands:") -for cmd in prod_commands: - print(f" {cmd}") -``` - -### Testing Integration - -```python -import pytest -from dotenv import get_cli_string -import subprocess - -class TestEnvironmentCommands: - """Test environment command generation.""" - - def test_basic_commands(self): - """Test basic command generation.""" - assert get_cli_string(action='list') == 'dotenv list' - assert get_cli_string(action='get', key='TEST') == 'dotenv get TEST' - - def test_file_path_commands(self): - """Test commands with file paths.""" - cmd = get_cli_string(path='test.env', action='list') - assert cmd == 'dotenv -f test.env list' - - def test_quote_modes(self): - """Test different quote modes.""" - cmd = get_cli_string(action='set', key='KEY', value='value', quote='never') - assert 'never' in cmd - - def test_command_execution(self, tmp_path): - """Test actual command execution.""" - env_file = tmp_path / '.env' - - # Generate and execute set command - set_cmd = get_cli_string( - path=str(env_file), - action='set', - key='TEST_KEY', - value='test_value' - ) - - subprocess.run(set_cmd, shell=True, check=True) - - # Verify the key was set - get_cmd = get_cli_string(path=str(env_file), action='get', key='TEST_KEY') - result = subprocess.run(get_cmd, shell=True, capture_output=True, text=True) - - assert result.stdout.strip() == 'test_value' -``` - -## Command String Format - -The generated command strings follow this format: - -```bash -dotenv [global_options] action [action_arguments] -``` - -Where: -- **global_options**: `-q quote_mode`, `-f file_path` -- **action**: `list`, `get`, `set`, `unset`, `run` -- **action_arguments**: Depend on the specific action - -The function ensures proper ordering and quoting of arguments to generate valid shell commands. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-python-dotenv/tile.json b/.tessl/tiles/tessl/pypi-python-dotenv/tile.json deleted file mode 100644 index 7285a3a59..000000000 --- a/.tessl/tiles/tessl/pypi-python-dotenv/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-python-dotenv", - "version": "1.1.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/python-dotenv@1.1.1", - "summary": "Read key-value pairs from a .env file and set them as environment variables" -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/adapters.md b/.tessl/tiles/tessl/pypi-requests/docs/adapters.md deleted file mode 100644 index 500785a7a..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/adapters.md +++ /dev/null @@ -1,571 +0,0 @@ -# Transport Adapters - -Transport adapters handle the actual HTTP communication, providing the interface between requests and underlying HTTP libraries. Adapters enable connection pooling, SSL/TLS handling, and protocol-specific optimizations. - -## Capabilities - -### BaseAdapter Class - -Abstract base class that all transport adapters inherit from. - -```python { .api } -class BaseAdapter: - """ - The base adapter class for transports. - - All transport adapters should inherit from this class. - """ - - def __init__(self): - """Initialize the adapter.""" - - def send(self, request, stream=False, timeout=None, verify=True, - cert=None, proxies=None) -> 'Response': - """ - Send a PreparedRequest and return a Response. - - Parameters: - - request: PreparedRequest object to send - - stream: Whether to stream the response content - - timeout: Timeout value in seconds - - verify: SSL verification setting - - cert: Client certificate - - proxies: Proxy configuration - - Returns: - Response object - - Raises: - NotImplementedError: Must be implemented by subclasses - """ - - def close(self): - """ - Clean up adapter resources. - - Called when session is closed. - """ -``` - -### HTTPAdapter Class - -Built-in HTTP/HTTPS adapter using urllib3 for connection pooling and transport. - -```python { .api } -class HTTPAdapter(BaseAdapter): - """ - Built-in HTTP adapter using urllib3. - - Provides connection pooling, SSL/TLS handling, and HTTP protocol support. - """ - - def __init__(self, pool_connections=10, pool_maxsize=10, max_retries=0, - pool_block=False): - """ - Initialize HTTPAdapter. - - Parameters: - - pool_connections: Number of urllib3 connection pools to cache - - pool_maxsize: Maximum connections per pool - - max_retries: Maximum number of retries per request - - pool_block: Whether to block when pool is full - """ - - def send(self, request, stream=False, timeout=None, verify=True, - cert=None, proxies=None) -> 'Response': - """ - Send a request using urllib3. - - Parameters: - - request: PreparedRequest to send - - stream: Stream response content - - timeout: Request timeout (connect, read) tuple or single value - - verify: SSL certificate verification (bool or CA bundle path) - - cert: Client certificate (path or (cert, key) tuple) - - proxies: Proxy configuration dict - - Returns: - Response object - """ - - def close(self): - """Close all pooled connections.""" - - # Connection management - def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): - """ - Initialize urllib3 PoolManager. - - Parameters: - - connections: Number of connection pools - - maxsize: Maximum connections per pool - - block: Whether to block when pool is full - - **pool_kwargs: Additional pool arguments - """ - - def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): - """ - Get connection pool with TLS context. - - Parameters: - - request: Request object - - verify: SSL verification setting - - proxies: Proxy configuration - - cert: Client certificate - - Returns: - ConnectionPool instance - """ - - def get_connection(self, url, proxies=None): - """ - DEPRECATED: Get connection for URL. - - Parameters: - - url: URL to get connection for - - proxies: Proxy configuration - - Returns: - ConnectionPool instance - """ - - def proxy_manager_for(self, proxy, **proxy_kwargs): - """ - Get ProxyManager for proxy URL. - - Parameters: - - proxy: Proxy URL - - **proxy_kwargs: Additional proxy arguments - - Returns: - ProxyManager instance - """ - - # Request/Response processing - def build_response(self, req, resp) -> 'Response': - """ - Build Response object from urllib3 response. - - Parameters: - - req: PreparedRequest object - - resp: urllib3 HTTPResponse object - - Returns: - Response object - """ - - def request_url(self, request, proxies) -> str: - """ - Get the URL to use for the request. - - Parameters: - - request: PreparedRequest object - - proxies: Proxy configuration - - Returns: - URL string to use - """ - - def add_headers(self, request, **kwargs): - """ - Add headers to the request. - - Parameters: - - request: PreparedRequest object - - **kwargs: Additional arguments - """ - - def proxy_headers(self, proxy) -> dict: - """ - Get headers to add for proxy requests. - - Parameters: - - proxy: Proxy URL - - Returns: - Dict of headers - """ - - # SSL/TLS handling - def cert_verify(self, conn, url, verify, cert): - """ - Verify SSL certificates and configure client certs. - - Parameters: - - conn: Connection object - - url: Request URL - - verify: SSL verification setting - - cert: Client certificate - """ - - def build_connection_pool_key_attributes(self, request, verify, cert=None) -> tuple: - """ - Build key attributes for connection pooling. - - Parameters: - - request: PreparedRequest object - - verify: SSL verification setting - - cert: Client certificate - - Returns: - Tuple of (pool_kwargs, connection_pool_kwargs) - """ -``` - -### Adapter Constants - -```python { .api } -DEFAULT_POOLBLOCK: bool # False -DEFAULT_POOLSIZE: int # 10 -DEFAULT_RETRIES: int # 0 -DEFAULT_POOL_TIMEOUT: None # None -``` - -## Usage Examples - -### Basic Adapter Usage - -```python -import requests -from requests.adapters import HTTPAdapter - -# Adapters are used automatically -response = requests.get('https://httpbin.org/get') -print(f"Status: {response.status_code}") - -# Access session adapters -session = requests.Session() -print("Mounted adapters:") -for prefix, adapter in session.adapters.items(): - print(f" {prefix}: {adapter}") -``` - -### Custom Adapter Configuration - -```python -import requests -from requests.adapters import HTTPAdapter - -# Create adapter with custom settings -adapter = HTTPAdapter( - pool_connections=20, # More connection pools - pool_maxsize=50, # More connections per pool - max_retries=3, # Retry failed requests - pool_block=True # Wait when pool is full -) - -# Mount adapter to session -session = requests.Session() -session.mount('https://', adapter) -session.mount('http://', adapter) - -# Requests will use the custom adapter -response = session.get('https://httpbin.org/get') -``` - -### Protocol-Specific Adapters - -```python -import requests -from requests.adapters import HTTPAdapter - -# Different adapters for different hosts -class APIAdapter(HTTPAdapter): - """Custom adapter for API endpoints.""" - - def __init__(self, api_key, **kwargs): - self.api_key = api_key - super().__init__(**kwargs) - - def add_headers(self, request, **kwargs): - request.headers['Authorization'] = f'Bearer {self.api_key}' - super().add_headers(request, **kwargs) - -# Mount custom adapter -session = requests.Session() -api_adapter = APIAdapter(api_key='your-api-key', max_retries=3) -session.mount('https://api.example.com/', api_adapter) - -# Regular adapter for other URLs -session.mount('https://', HTTPAdapter(max_retries=1)) - -# Different adapters used based on URL -api_response = session.get('https://api.example.com/data') # Uses APIAdapter -web_response = session.get('https://other-site.com/page') # Uses HTTPAdapter -``` - -### Connection Pool Management - -```python -import requests -from requests.adapters import HTTPAdapter - -# Configure connection pooling -adapter = HTTPAdapter( - pool_connections=10, # 10 connection pools - pool_maxsize=100, # 100 connections per pool - pool_block=False # Don't block when pool full -) - -session = requests.Session() -session.mount('https://', adapter) - -# Make many requests - connections are pooled and reused -urls = [f'https://httpbin.org/delay/{i}' for i in range(5)] - -for url in urls: - response = session.get(url) - print(f"Response from {url}: {response.status_code}") - -# Close session to clean up connection pools -session.close() -``` - -### SSL/TLS Configuration - -```python -import requests -from requests.adapters import HTTPAdapter - -# Custom adapter with SSL settings -class SecureAdapter(HTTPAdapter): - def cert_verify(self, conn, url, verify, cert): - # Custom SSL verification logic - super().cert_verify(conn, url, verify, cert) - print(f"SSL verification for {url}: verify={verify}") - -# Use secure adapter -session = requests.Session() -session.mount('https://', SecureAdapter()) - -# Configure SSL verification -response = session.get('https://httpbin.org/get', - verify=True, # Verify SSL certificates - cert=None) # No client certificate -``` - -### Retry Configuration - -```python -import requests -from requests.adapters import HTTPAdapter -from urllib3.util.retry import Retry - -# Configure retry strategy -retry_strategy = Retry( - total=3, # Total retries - status_forcelist=[429, 500, 502, 503, 504], # Status codes to retry - method_whitelist=["HEAD", "GET", "OPTIONS"], # Methods to retry - backoff_factor=1 # Backoff between retries -) - -# Create adapter with retry strategy -adapter = HTTPAdapter(max_retries=retry_strategy) - -session = requests.Session() -session.mount('http://', adapter) -session.mount('https://', adapter) - -# Requests will automatically retry on failures -response = session.get('https://httpbin.org/status/500') -``` - -### Custom Transport Adapter - -```python -import requests -from requests.adapters import BaseAdapter -from requests.models import Response - -class MockAdapter(BaseAdapter): - """Mock adapter for testing.""" - - def __init__(self, responses=None): - super().__init__() - self.responses = responses or {} - - def send(self, request, **kwargs): - """Return mock response.""" - response = Response() - response.status_code = 200 - response.headers['Content-Type'] = 'application/json' - - # Mock response based on URL - if request.url in self.responses: - response._content = self.responses[request.url].encode('utf-8') - else: - response._content = b'{"mock": "response"}' - - response.url = request.url - response.request = request - return response - - def close(self): - pass - -# Use mock adapter for testing -mock_responses = { - 'https://api.example.com/data': '{"users": [{"name": "John"}]}' -} - -session = requests.Session() -session.mount('https://api.example.com/', MockAdapter(mock_responses)) - -# This returns the mock response -response = session.get('https://api.example.com/data') -print(response.json()) # {'users': [{'name': 'John'}]} -``` - -### Proxy Adapter Configuration - -```python -import requests -from requests.adapters import HTTPAdapter - -class ProxyAdapter(HTTPAdapter): - """Adapter with proxy configuration.""" - - def __init__(self, proxy_url, **kwargs): - self.proxy_url = proxy_url - super().__init__(**kwargs) - - def proxy_headers(self, proxy): - """Add custom proxy headers.""" - headers = super().proxy_headers(proxy) - headers['Proxy-Authorization'] = 'Basic dXNlcjpwYXNz' # base64 user:pass - return headers - -# Configure proxy adapter -proxy_adapter = ProxyAdapter('http://proxy.example.com:8080') - -session = requests.Session() -session.mount('http://', proxy_adapter) -session.mount('https://', proxy_adapter) - -# Requests go through the proxy -response = session.get('https://httpbin.org/ip') -print(response.json()) # Shows proxy IP -``` - -### Performance Monitoring Adapter - -```python -import requests -import time -from requests.adapters import HTTPAdapter - -class TimingAdapter(HTTPAdapter): - """Adapter that measures request timing.""" - - def send(self, request, **kwargs): - start_time = time.time() - response = super().send(request, **kwargs) - end_time = time.time() - - # Add timing information to response - response.elapsed_total = end_time - start_time - print(f"Request to {request.url} took {response.elapsed_total:.3f}s") - - return response - -# Use timing adapter -session = requests.Session() -session.mount('https://', TimingAdapter()) - -response = session.get('https://httpbin.org/delay/2') -print(f"Total time: {response.elapsed_total:.3f}s") -``` - -### Adapter Debugging - -```python -import requests -from requests.adapters import HTTPAdapter - -class DebugAdapter(HTTPAdapter): - """Adapter with detailed logging.""" - - def send(self, request, **kwargs): - print(f"Sending {request.method} request to {request.url}") - print(f"Headers: {dict(request.headers)}") - - if request.body: - print(f"Body: {request.body[:100]}...") - - response = super().send(request, **kwargs) - - print(f"Received {response.status_code} response") - print(f"Response headers: {dict(response.headers)}") - - return response - -# Use debug adapter -session = requests.Session() -session.mount('https://', DebugAdapter()) - -response = session.post('https://httpbin.org/post', - json={'key': 'value'}) -``` - -## Adapter Best Practices - -### Resource Management - -```python -import requests -from requests.adapters import HTTPAdapter - -# Always close sessions to clean up connection pools -session = requests.Session() -adapter = HTTPAdapter(pool_connections=10, pool_maxsize=50) -session.mount('https://', adapter) - -try: - # Use session - response = session.get('https://api.example.com/data') - # Process response... -finally: - # Ensure cleanup - session.close() - -# Or use context manager -with requests.Session() as session: - adapter = HTTPAdapter(pool_connections=10, pool_maxsize=50) - session.mount('https://', adapter) - response = session.get('https://api.example.com/data') -# Session automatically closed -``` - -### Adapter Selection - -```python -import requests -from requests.adapters import HTTPAdapter - -# Different configurations for different services -session = requests.Session() - -# High-performance adapter for API calls -api_adapter = HTTPAdapter( - pool_connections=20, - pool_maxsize=100, - max_retries=3 -) - -# Conservative adapter for file downloads -download_adapter = HTTPAdapter( - pool_connections=5, - pool_maxsize=10, - max_retries=1 -) - -# Mount adapters with specific prefixes -session.mount('https://api.fastservice.com/', api_adapter) -session.mount('https://downloads.example.com/', download_adapter) -session.mount('https://', HTTPAdapter()) # Default adapter - -# Requests automatically use appropriate adapter -api_response = session.get('https://api.fastservice.com/data') -file_response = session.get('https://downloads.example.com/file.zip') -other_response = session.get('https://other-site.com/page') -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/authentication.md b/.tessl/tiles/tessl/pypi-requests/docs/authentication.md deleted file mode 100644 index b274af7ca..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/authentication.md +++ /dev/null @@ -1,344 +0,0 @@ -# Authentication - -Authentication handlers for various HTTP authentication schemes. The requests library provides built-in support for the most common authentication methods and allows for custom authentication handlers. - -## Capabilities - -### Base Authentication Class - -All authentication handlers inherit from the AuthBase class. - -```python { .api } -class AuthBase: - """Base class that all authentication implementations derive from.""" - - def __call__(self, request): - """ - Apply authentication to a request. - - Parameters: - - request: PreparedRequest object to modify - - Returns: - Modified PreparedRequest object - """ -``` - -### HTTP Basic Authentication - -Implements HTTP Basic Authentication using username and password. - -```python { .api } -class HTTPBasicAuth(AuthBase): - """Attaches HTTP Basic Authentication to the given Request object.""" - - def __init__(self, username: str, password: str): - """ - Initialize Basic Authentication. - - Parameters: - - username: Username for authentication - - password: Password for authentication - """ - - def __call__(self, request) -> 'PreparedRequest': - """Apply Basic Authentication to request.""" - - def __eq__(self, other) -> bool: - """Check equality with another auth object.""" - - def __ne__(self, other) -> bool: - """Check inequality with another auth object.""" -``` - -### HTTP Digest Authentication - -Implements HTTP Digest Authentication for enhanced security over Basic auth. - -```python { .api } -class HTTPDigestAuth(AuthBase): - """Attaches HTTP Digest Authentication to the given Request object.""" - - def __init__(self, username: str, password: str): - """ - Initialize Digest Authentication. - - Parameters: - - username: Username for authentication - - password: Password for authentication - """ - - def __call__(self, request) -> 'PreparedRequest': - """Apply Digest Authentication to request.""" -``` - -### HTTP Proxy Authentication - -Implements HTTP Proxy Authentication, extending Basic Authentication for proxy servers. - -```python { .api } -class HTTPProxyAuth(HTTPBasicAuth): - """Attaches HTTP Proxy Authentication to a given Request object.""" - - def __call__(self, request) -> 'PreparedRequest': - """Apply Proxy Authentication to request.""" -``` - -## Usage Examples - -### Basic Authentication - -```python -import requests -from requests.auth import HTTPBasicAuth - -# Using auth tuple (shorthand) -response = requests.get('https://api.example.com/data', auth=('username', 'password')) - -# Using HTTPBasicAuth class -basic_auth = HTTPBasicAuth('username', 'password') -response = requests.get('https://api.example.com/data', auth=basic_auth) - -# With sessions for persistent auth -session = requests.Session() -session.auth = ('username', 'password') -response = session.get('https://api.example.com/data') -``` - -### Digest Authentication - -```python -import requests -from requests.auth import HTTPDigestAuth - -# Digest authentication for enhanced security -digest_auth = HTTPDigestAuth('username', 'password') -response = requests.get('https://api.example.com/secure', auth=digest_auth) - -# With sessions -session = requests.Session() -session.auth = HTTPDigestAuth('user', 'pass') -response = session.get('https://api.example.com/data') -``` - -### Proxy Authentication - -```python -import requests -from requests.auth import HTTPProxyAuth - -# Authenticate with proxy server -proxy_auth = HTTPProxyAuth('proxy_user', 'proxy_pass') -proxies = {'http': 'http://proxy.example.com:8080'} - -response = requests.get('https://api.example.com/data', - auth=proxy_auth, - proxies=proxies) -``` - -### Custom Authentication - -```python -import requests -from requests.auth import AuthBase - -class TokenAuth(AuthBase): - """Custom token-based authentication.""" - - def __init__(self, token): - self.token = token - - def __call__(self, request): - request.headers['Authorization'] = f'Bearer {self.token}' - return request - -# Use custom authentication -token_auth = TokenAuth('your-api-token') -response = requests.get('https://api.example.com/data', auth=token_auth) -``` - -### OAuth 1.0 Authentication - -```python -# Note: OAuth requires additional libraries like requests-oauthlib -from requests_oauthlib import OAuth1 - -# OAuth 1.0 authentication -oauth = OAuth1('client_key', 'client_secret', 'resource_owner_key', 'resource_owner_secret') -response = requests.get('https://api.twitter.com/1.1/account/verify_credentials.json', auth=oauth) -``` - -### Bearer Token Authentication - -```python -import requests - -# Simple bearer token authentication -headers = {'Authorization': 'Bearer your-access-token'} -response = requests.get('https://api.example.com/data', headers=headers) - -# Or create a custom auth class -class BearerAuth(requests.auth.AuthBase): - def __init__(self, token): - self.token = token - - def __call__(self, r): - r.headers["Authorization"] = f"Bearer {self.token}" - return r - -# Use the custom auth -bearer_auth = BearerAuth('your-access-token') -response = requests.get('https://api.example.com/data', auth=bearer_auth) -``` - -### Multiple Authentication Methods - -```python -import requests -from requests.auth import HTTPBasicAuth, HTTPDigestAuth - -# Try different auth methods -def authenticate_with_fallback(url, username, password): - # Try Digest first (more secure) - try: - response = requests.get(url, auth=HTTPDigestAuth(username, password)) - if response.status_code == 200: - return response - except requests.exceptions.RequestException: - pass - - # Fall back to Basic auth - try: - response = requests.get(url, auth=HTTPBasicAuth(username, password)) - return response - except requests.exceptions.RequestException: - pass - - # Try without auth - return requests.get(url) - -response = authenticate_with_fallback('https://api.example.com/data', 'user', 'pass') -``` - -### Session-based Authentication - -```python -import requests - -# Login to get session cookie -session = requests.Session() -login_data = {'username': 'user', 'password': 'pass'} -session.post('https://example.com/login', data=login_data) - -# Subsequent requests use the session cookie -response = session.get('https://example.com/protected-data') - -# Or combine with other auth methods -session.auth = ('api_user', 'api_pass') -response = session.get('https://api.example.com/data') -``` - -## Authentication with Different Request Types - -### GET with Authentication - -```python -import requests - -# Simple GET with auth -response = requests.get('https://api.example.com/users', auth=('user', 'pass')) -users = response.json() -``` - -### POST with Authentication - -```python -import requests - -# POST data with authentication -data = {'name': 'John Doe', 'email': 'john@example.com'} -response = requests.post('https://api.example.com/users', - json=data, - auth=('admin', 'password')) -``` - -### File Upload with Authentication - -```python -import requests - -# Upload file with authentication -files = {'file': open('document.pdf', 'rb')} -response = requests.post('https://api.example.com/upload', - files=files, - auth=('user', 'pass')) -``` - -## Security Considerations - -### HTTPS Requirement - -Always use HTTPS when sending authentication credentials: - -```python -import requests - -# Good - HTTPS protects credentials -response = requests.get('https://api.example.com/data', auth=('user', 'pass')) - -# Bad - HTTP exposes credentials -# response = requests.get('http://api.example.com/data', auth=('user', 'pass')) -``` - -### Environment Variables - -Store credentials in environment variables instead of hardcoding: - -```python -import os -import requests - -username = os.environ.get('API_USERNAME') -password = os.environ.get('API_PASSWORD') - -if username and password: - response = requests.get('https://api.example.com/data', auth=(username, password)) -else: - raise ValueError("Missing authentication credentials") -``` - -### Certificate Verification - -Always verify SSL certificates in production: - -```python -import requests - -# Default behavior - verify SSL certificates -response = requests.get('https://api.example.com/data', - auth=('user', 'pass'), - verify=True) # This is the default - -# Only disable verification for testing -# response = requests.get('https://api.example.com/data', -# auth=('user', 'pass'), -# verify=False) # Only for testing! -``` - -## Authentication Utility Function - -```python { .api } -def _basic_auth_str(username: str, password: str) -> str: - """ - Generate a Basic Auth string. - - Parameters: - - username: Username for authentication - - password: Password for authentication - - Returns: - Basic authentication header value - """ -``` - -This utility function is used internally by HTTPBasicAuth but can be useful for custom authentication implementations. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/cookies.md b/.tessl/tiles/tessl/pypi-requests/docs/cookies.md deleted file mode 100644 index 784f9e30c..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/cookies.md +++ /dev/null @@ -1,398 +0,0 @@ -# Cookie Handling - -Cookie management functionality providing a dict-like interface for handling HTTP cookies with compatibility for both client and server-side cookie operations. - -## Capabilities - -### RequestsCookieJar Class - -A CookieJar with dict-like interface that extends standard cookielib functionality. - -```python { .api } -class RequestsCookieJar: - """ - Compatibility class for http.cookiejar.CookieJar with dict-like interface. - - Provides convenient access to cookies while maintaining full CookieJar functionality. - """ - - def __init__(self, policy=None): - """ - Initialize RequestsCookieJar. - - Parameters: - - policy: CookiePolicy instance (optional) - """ - - # Dict-like interface - def get(self, name: str, default=None, domain=None, path=None) -> str: - """ - Get cookie value by name. - - Parameters: - - name: Cookie name - - default: Default value if cookie not found - - domain: Specific domain to search (optional) - - path: Specific path to search (optional) - - Returns: - Cookie value or default - """ - - def set(self, name: str, value: str, **kwargs): - """ - Set a cookie. - - Parameters: - - name: Cookie name - - value: Cookie value - - **kwargs: Additional cookie attributes (domain, path, etc.) - - Returns: - Cookie object - """ - - def __getitem__(self, name: str) -> str: - """Get cookie value by name.""" - - def __setitem__(self, name: str, value: str): - """Set cookie value by name.""" - - def __delitem__(self, name: str): - """Delete cookie by name.""" - - def __contains__(self, name: str) -> bool: - """Check if cookie exists.""" - - def update(self, other): - """Update cookies from another cookie jar or dict.""" - - def keys(self) -> list[str]: - """Get list of cookie names.""" - - def values(self) -> list[str]: - """Get list of cookie values.""" - - def items(self) -> list[tuple[str, str]]: - """Get list of (name, value) tuples.""" - - def iterkeys(self): - """Iterate over cookie names.""" - - def itervalues(self): - """Iterate over cookie values.""" - - def iteritems(self): - """Iterate over (name, value) tuples.""" - - # Cookie management methods - def list_domains(self) -> list[str]: - """Get list of domains with cookies.""" - - def list_paths(self) -> list[str]: - """Get list of paths with cookies.""" - - def multiple_domains(self) -> bool: - """Check if cookies exist for multiple domains.""" - - def get_dict(self, domain=None, path=None) -> dict: - """ - Get cookies as a plain dict. - - Parameters: - - domain: Filter by domain (optional) - - path: Filter by path (optional) - - Returns: - Dict of cookie name -> value mappings - """ - - def copy(self) -> 'RequestsCookieJar': - """Create a copy of the cookie jar.""" - - def get_policy(self): - """Get the cookie policy.""" -``` - -### Cookie Utility Functions - -Functions for cookie manipulation and conversion. - -```python { .api } -def extract_cookies_to_jar(jar, request, response): - """ - Extract cookies from response and add to jar. - - Parameters: - - jar: RequestsCookieJar to add cookies to - - request: Request object - - response: Response object - """ - -def get_cookie_header(jar, request) -> str | None: - """ - Get Cookie header value from jar for request. - - Parameters: - - jar: RequestsCookieJar containing cookies - - request: Request object - - Returns: - Cookie header value or None - """ - -def remove_cookie_by_name(cookiejar, name: str, domain=None, path=None): - """ - Remove cookie from jar by name. - - Parameters: - - cookiejar: CookieJar to remove from - - name: Cookie name to remove - - domain: Domain filter (optional) - - path: Path filter (optional) - """ - -def create_cookie(name: str, value: str, **kwargs): - """ - Create a Cookie object. - - Parameters: - - name: Cookie name - - value: Cookie value - - **kwargs: Additional cookie attributes - - Returns: - Cookie object - """ - -def morsel_to_cookie(morsel): - """ - Convert http.cookies.Morsel to Cookie object. - - Parameters: - - morsel: Morsel object to convert - - Returns: - Cookie object - """ - -def cookiejar_from_dict(cookie_dict: dict, cookiejar=None, overwrite=True): - """ - Create CookieJar from dictionary. - - Parameters: - - cookie_dict: Dict of cookie name -> value mappings - - cookiejar: Existing jar to add to (optional) - - overwrite: Whether to overwrite existing cookies - - Returns: - CookieJar containing the cookies - """ - -def merge_cookies(cookiejar, cookies): - """ - Merge cookies into an existing jar. - - Parameters: - - cookiejar: Target CookieJar - - cookies: Cookies to merge (dict or CookieJar) - - Returns: - Updated CookieJar - """ -``` - -### Cookie Exceptions - -```python { .api } -class CookieConflictError(RuntimeError): - """Raised when multiple cookies match the same criteria.""" -``` - -## Usage Examples - -### Basic Cookie Operations - -```python -import requests - -# Cookies are automatically handled -response = requests.get('https://httpbin.org/cookies/set/sessionid/abc123') -print(response.cookies['sessionid']) # 'abc123' - -# Access cookie jar -jar = response.cookies -print(jar.get('sessionid')) # 'abc123' -print('sessionid' in jar) # True - -# Send cookies with request -cookies = {'user': 'john', 'token': 'xyz789'} -response = requests.get('https://httpbin.org/cookies', cookies=cookies) -``` - -### Session Cookie Persistence - -```python -import requests - -# Cookies persist across requests in a session -session = requests.Session() -session.get('https://httpbin.org/cookies/set/sessionid/abc123') - -# Cookie is automatically sent in subsequent requests -response = session.get('https://httpbin.org/cookies') -data = response.json() -print(data['cookies']['sessionid']) # 'abc123' -``` - -### Manual Cookie Management - -```python -import requests -from requests.cookies import RequestsCookieJar - -# Create custom cookie jar -jar = RequestsCookieJar() -jar.set('custom_cookie', 'custom_value', domain='example.com') -jar.set('another_cookie', 'another_value') - -# Use jar with request -response = requests.get('https://example.com', cookies=jar) - -# Examine cookies -for name, value in jar.items(): - print(f"{name}: {value}") - -# Get cookies for specific domain -domain_cookies = jar.get_dict(domain='example.com') -print(domain_cookies) -``` - -### Cookie Jar Operations - -```python -import requests - -# Get cookies from response -response = requests.get('https://httpbin.org/cookies/set/foo/bar') -jar = response.cookies - -# Cookie jar behaves like a dict -print(jar['foo']) # 'bar' -print(jar.get('foo')) # 'bar' -print(list(jar.keys())) # ['foo'] -print(list(jar.values())) # ['bar'] -print(list(jar.items())) # [('foo', 'bar')] - -# Copy cookie jar -new_jar = jar.copy() - -# Update from dict -jar.update({'baz': 'qux'}) - -# Check domains and paths -print(jar.list_domains()) -print(jar.list_paths()) -print(jar.multiple_domains()) -``` - -### Cookie Utilities - -```python -import requests -from requests.cookies import cookiejar_from_dict, merge_cookies - -# Create jar from dict -cookie_dict = {'session': 'abc123', 'user': 'john'} -jar = cookiejar_from_dict(cookie_dict) - -# Use with session -session = requests.Session() -session.cookies = jar - -# Merge cookies -additional_cookies = {'theme': 'dark', 'lang': 'en'} -merge_cookies(jar, additional_cookies) - -response = session.get('https://httpbin.org/cookies') -``` - -### Advanced Cookie Handling - -```python -import requests -from requests.cookies import RequestsCookieJar - -def manage_cookies(): - jar = RequestsCookieJar() - - # Set cookies with attributes - jar.set('secure_cookie', 'secret_value', - domain='api.example.com', - path='/secure', - secure=True) - - # Set session cookie (no expiry) - jar.set('session_id', 'sess_123456') - - # Check cookie existence - if 'session_id' in jar: - print("Session cookie found") - - # Get cookie with domain/path filtering - secure_value = jar.get('secure_cookie', - domain='api.example.com', - path='/secure') - - # Remove specific cookie - if 'old_cookie' in jar: - del jar['old_cookie'] - - return jar - -# Use custom jar -jar = manage_cookies() -response = requests.get('https://api.example.com/secure/data', cookies=jar) -``` - -## Cookie Security - -### Secure Cookie Practices - -```python -import requests -from requests.cookies import RequestsCookieJar - -# Always use HTTPS for sensitive cookies -jar = RequestsCookieJar() -jar.set('auth_token', 'sensitive_token', - domain='secure-api.com', - secure=True, # Only send over HTTPS - httponly=True) # Not accessible via JavaScript - -# Use with secure request -response = requests.get('https://secure-api.com/data', cookies=jar) -``` - -### Cookie Domain Validation - -```python -import requests - -def safe_cookie_handling(url, cookies): - """Safely handle cookies with domain validation.""" - session = requests.Session() - - # Let requests handle cookie domain validation - response = session.get(url, cookies=cookies) - - # Check which cookies were actually sent - request_cookies = response.request.headers.get('Cookie', '') - print(f"Cookies sent: {request_cookies}") - - return response - -# Example usage -cookies = {'token': 'abc123'} -response = safe_cookie_handling('https://api.example.com', cookies) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/exceptions.md b/.tessl/tiles/tessl/pypi-requests/docs/exceptions.md deleted file mode 100644 index f0ea58896..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/exceptions.md +++ /dev/null @@ -1,430 +0,0 @@ -# Exception Handling - -Comprehensive exception hierarchy for handling various error conditions that can occur during HTTP requests. The requests library provides specific exceptions for different types of errors, allowing for precise error handling. - -## Capabilities - -### Base Exception - -All requests exceptions inherit from the base RequestException class. - -```python { .api } -class RequestException(IOError): - """ - Base exception for all request-related errors. - - Attributes: - - response: Response object (if available) - - request: Request object that caused the exception - """ - - def __init__(self, *args, **kwargs): - """ - Initialize RequestException. - - Parameters: - - response: Response object (optional) - - request: Request object (optional) - """ -``` - -### HTTP Errors - -Exceptions related to HTTP status codes and server responses. - -```python { .api } -class HTTPError(RequestException): - """ - An HTTP error occurred. - - Raised by Response.raise_for_status() for 4xx and 5xx status codes. - """ - -class InvalidJSONError(RequestException): - """A JSON error occurred.""" - -class JSONDecodeError(InvalidJSONError): - """Couldn't decode the text into JSON.""" -``` - -### Connection Errors - -Exceptions related to network connectivity and connection issues. - -```python { .api } -class ConnectionError(RequestException): - """A connection error occurred.""" - -class ProxyError(ConnectionError): - """A proxy error occurred.""" - -class SSLError(ConnectionError): - """An SSL error occurred.""" -``` - -### Timeout Errors - -Exceptions related to request timeouts. - -```python { .api } -class Timeout(RequestException): - """ - The request timed out. - - Catching this error will catch both ConnectTimeout and ReadTimeout. - """ - -class ConnectTimeout(ConnectionError, Timeout): - """ - The request timed out while trying to connect to the remote server. - - Requests that produced this error are safe to retry. - """ - -class ReadTimeout(Timeout): - """The server did not send any data in the allotted amount of time.""" -``` - -### URL and Request Errors - -Exceptions related to malformed URLs and invalid requests. - -```python { .api } -class URLRequired(RequestException): - """A valid URL is required to make a request.""" - -class MissingSchema(RequestException, ValueError): - """The URL scheme (e.g. http or https) is missing.""" - -class InvalidSchema(RequestException, ValueError): - """The URL scheme is invalid.""" - -class InvalidURL(RequestException, ValueError): - """The URL provided is invalid.""" - -class InvalidHeader(RequestException, ValueError): - """The header provided is invalid.""" - -class InvalidProxyURL(InvalidURL): - """The proxy URL provided is invalid.""" -``` - -### Redirect Errors - -Exceptions related to HTTP redirects. - -```python { .api } -class TooManyRedirects(RequestException): - """Too many redirects occurred.""" -``` - -### Content and Encoding Errors - -Exceptions related to response content processing. - -```python { .api } -class ChunkedEncodingError(RequestException): - """The server declared chunked encoding but sent an invalid chunk.""" - -class ContentDecodingError(RequestException): - """Failed to decode response content.""" - -class StreamConsumedError(RequestException, TypeError): - """The content for this response was already consumed.""" - -class RetryError(RequestException): - """Custom retries logic failed.""" - -class UnrewindableBodyError(RequestException): - """The request body cannot be rewound for a retry.""" -``` - -### Warning Classes - -Warning classes for non-fatal issues. - -```python { .api } -class RequestsWarning(Warning): - """Base warning for requests-related warnings.""" - -class FileModeWarning(RequestsWarning, DeprecationWarning): - """Warning for file mode issues.""" - -class RequestsDependencyWarning(RequestsWarning): - """Warning for dependency-related issues.""" -``` - -## Usage Examples - -### Basic Exception Handling - -```python -import requests -from requests.exceptions import RequestException, HTTPError, ConnectionError, Timeout - -try: - response = requests.get('https://api.example.com/data', timeout=5) - response.raise_for_status() # Raises HTTPError for bad status codes - data = response.json() -except HTTPError as e: - print(f"HTTP error occurred: {e}") - print(f"Status code: {e.response.status_code}") -except ConnectionError as e: - print(f"Connection error occurred: {e}") -except Timeout as e: - print(f"Request timed out: {e}") -except RequestException as e: - print(f"An error occurred: {e}") -``` - -### Specific Exception Handling - -```python -import requests -from requests.exceptions import ( - ConnectTimeout, ReadTimeout, SSLError, - JSONDecodeError, TooManyRedirects -) - -def robust_api_call(url, max_retries=3): - for attempt in range(max_retries): - try: - response = requests.get(url, timeout=(5, 10)) # (connect, read) timeout - response.raise_for_status() - return response.json() - - except ConnectTimeout: - print(f"Connection timeout on attempt {attempt + 1}") - if attempt == max_retries - 1: - raise - - except ReadTimeout: - print(f"Read timeout on attempt {attempt + 1}") - if attempt == max_retries - 1: - raise - - except SSLError as e: - print(f"SSL error: {e}") - # SSL errors usually shouldn't be retried - raise - - except JSONDecodeError: - print("Response is not valid JSON") - # Try to return raw text instead - return response.text - - except TooManyRedirects: - print("Too many redirects") - raise - - except HTTPError as e: - if e.response.status_code >= 500: - # Server errors might be temporary, retry - print(f"Server error {e.response.status_code} on attempt {attempt + 1}") - if attempt == max_retries - 1: - raise - else: - # Client errors usually shouldn't be retried - print(f"Client error {e.response.status_code}") - raise - -result = robust_api_call('https://api.example.com/data') -``` - -### Connection Error Handling - -```python -import requests -from requests.exceptions import ConnectionError, ProxyError, SSLError - -def handle_connection_errors(url): - try: - response = requests.get(url) - return response - - except ProxyError as e: - print(f"Proxy error: {e}") - # Try without proxy - return requests.get(url, proxies={'http': '', 'https': ''}) - - except SSLError as e: - print(f"SSL error: {e}") - # Option 1: Use HTTP instead (not recommended for production) - # return requests.get(url.replace('https://', 'http://')) - - # Option 2: Disable SSL verification (not recommended for production) - # return requests.get(url, verify=False) - - # Option 3: Provide custom CA bundle - return requests.get(url, verify='/path/to/ca-bundle.crt') - - except ConnectionError as e: - print(f"Connection error: {e}") - raise # Re-raise if no recovery is possible -``` - -### URL and Request Error Handling - -```python -import requests -from requests.exceptions import ( - URLRequired, MissingSchema, InvalidSchema, - InvalidURL, InvalidHeader -) - -def validate_and_request(url, headers=None): - try: - response = requests.get(url, headers=headers) - return response - - except URLRequired: - raise ValueError("URL is required") - - except MissingSchema: - # Try adding https:// - return requests.get(f"https://{url}") - - except InvalidSchema as e: - print(f"Invalid URL scheme: {e}") - raise ValueError(f"URL must start with http:// or https://") - - except InvalidURL as e: - print(f"Invalid URL: {e}") - raise ValueError("Please provide a valid URL") - - except InvalidHeader as e: - print(f"Invalid header: {e}") - raise ValueError("Please check header format") - -# Example usage -try: - response = validate_and_request("example.com") # Missing schema -except ValueError as e: - print(f"Validation error: {e}") -``` - -### JSON Error Handling - -```python -import requests -from requests.exceptions import JSONDecodeError - -def safe_json_request(url): - try: - response = requests.get(url) - response.raise_for_status() - - # Try to parse JSON - return response.json() - - except JSONDecodeError as e: - print(f"JSON decode error: {e}") - print(f"Response content: {response.text[:200]}...") # First 200 chars - - # Return raw text or None based on content type - content_type = response.headers.get('content-type', '') - if 'json' in content_type.lower(): - # Expected JSON but got invalid JSON - raise ValueError("Expected JSON response but got invalid JSON") - else: - # Not JSON, return text - return response.text - -data = safe_json_request('https://api.example.com/data') -``` - -### Comprehensive Error Handling Pattern - -```python -import requests -from requests.exceptions import RequestException -import time - -def reliable_request(url, max_retries=3, backoff_factor=1): - """ - Make a reliable HTTP request with exponential backoff retry logic. - """ - last_exception = None - - for attempt in range(max_retries): - try: - response = requests.get(url, timeout=(5, 30)) - response.raise_for_status() - return response - - except requests.exceptions.ConnectTimeout: - print(f"Connect timeout on attempt {attempt + 1}") - last_exception = sys.exc_info()[1] - - except requests.exceptions.ReadTimeout: - print(f"Read timeout on attempt {attempt + 1}") - last_exception = sys.exc_info()[1] - - except requests.exceptions.HTTPError as e: - if 500 <= e.response.status_code < 600: - # Server error - retry - print(f"Server error {e.response.status_code} on attempt {attempt + 1}") - last_exception = e - else: - # Client error - don't retry - raise - - except requests.exceptions.ConnectionError: - print(f"Connection error on attempt {attempt + 1}") - last_exception = sys.exc_info()[1] - - except RequestException as e: - # Other request exceptions - print(f"Request exception on attempt {attempt + 1}: {e}") - last_exception = e - - # Wait before retrying (exponential backoff) - if attempt < max_retries - 1: - wait_time = backoff_factor * (2 ** attempt) - print(f"Waiting {wait_time} seconds before retry...") - time.sleep(wait_time) - - # All retries failed - raise last_exception - -# Usage -try: - response = reliable_request('https://api.example.com/unreliable-endpoint') - data = response.json() -except Exception as e: - print(f"Request ultimately failed: {e}") -``` - -### Exception Information Access - -```python -import requests -from requests.exceptions import HTTPError, RequestException - -try: - response = requests.get('https://httpbin.org/status/404') - response.raise_for_status() -except HTTPError as e: - print(f"HTTP Error: {e}") - print(f"Status Code: {e.response.status_code}") - print(f"Response Text: {e.response.text}") - print(f"Request URL: {e.request.url}") - print(f"Request Method: {e.request.method}") -except RequestException as e: - print(f"Request Exception: {e}") - if hasattr(e, 'response') and e.response is not None: - print(f"Response Status: {e.response.status_code}") - if hasattr(e, 'request') and e.request is not None: - print(f"Request URL: {e.request.url}") -``` - -## Best Practices - -1. **Always catch specific exceptions** before more general ones -2. **Use `response.raise_for_status()`** to automatically raise HTTPError for bad status codes -3. **Set appropriate timeouts** to avoid hanging requests -4. **Implement retry logic** for transient errors (timeouts, 5xx errors) -5. **Don't retry client errors** (4xx status codes) as they indicate request problems -6. **Log exception details** for debugging -7. **Provide meaningful error messages** to users -8. **Handle SSL errors carefully** - avoid disabling verification in production \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/hooks.md b/.tessl/tiles/tessl/pypi-requests/docs/hooks.md deleted file mode 100644 index fd81a40ec..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/hooks.md +++ /dev/null @@ -1,471 +0,0 @@ -# Event Hooks - -Event hook system that allows custom functions to be called at specific points during request processing. Hooks provide a way to modify requests and responses or implement custom logging and monitoring. - -## Capabilities - -### Hook System - -The requests library provides a simple but powerful hook system for intercepting and modifying HTTP requests and responses. - -```python { .api } -# Available hook events -HOOKS: list[str] # ['response'] - -def default_hooks() -> dict: - """ - Return the default hooks structure. - - Returns: - Dict with default hook configuration - """ - -def dispatch_hook(key: str, hooks: dict, hook_data, **kwargs): - """ - Execute hooks for a given event. - - Parameters: - - key: Hook event name - - hooks: Hook configuration dict - - hook_data: Data to pass to hook functions - - **kwargs: Additional arguments for hooks - - Returns: - Result from hook execution - """ -``` - -### Hook Events - -Currently, requests supports one hook event: - -- **response**: Called after receiving a response, allows modifying the Response object - -## Usage Examples - -### Basic Hook Usage - -```python -import requests - -def response_hook(response, *args, **kwargs): - """Custom response hook function.""" - print(f"Received response: {response.status_code} from {response.url}") - # Optionally modify the response - response.custom_processed = True - return response - -# Add hook to single request -response = requests.get('https://httpbin.org/get', - hooks={'response': response_hook}) - -print(hasattr(response, 'custom_processed')) # True -``` - -### Multiple Hooks - -```python -import requests - -def log_hook(response, *args, **kwargs): - """Log response details.""" - print(f"LOG: {response.request.method} {response.url} -> {response.status_code}") - return response - -def timing_hook(response, *args, **kwargs): - """Add timing information.""" - print(f"TIMING: Request took {response.elapsed.total_seconds():.3f} seconds") - return response - -def validation_hook(response, *args, **kwargs): - """Validate response.""" - if response.status_code >= 400: - print(f"WARNING: Error response {response.status_code}") - return response - -# Multiple hooks for the same event -hooks = { - 'response': [log_hook, timing_hook, validation_hook] -} - -response = requests.get('https://httpbin.org/status/404', hooks=hooks) -# Prints: -# LOG: GET https://httpbin.org/status/404 -> 404 -# TIMING: Request took 0.123 seconds -# WARNING: Error response 404 -``` - -### Session-Level Hooks - -```python -import requests - -def session_response_hook(response, *args, **kwargs): - """Hook applied to all session requests.""" - # Add custom header to all responses - response.headers['X-Custom-Processed'] = 'true' - - # Log all requests - print(f"Session request: {response.request.method} {response.url}") - - return response - -# Add hook to session - applies to all requests -session = requests.Session() -session.hooks['response'].append(session_response_hook) - -# All requests through this session will trigger the hook -response1 = session.get('https://httpbin.org/get') -response2 = session.post('https://httpbin.org/post', json={'key': 'value'}) - -print(response1.headers['X-Custom-Processed']) # 'true' -print(response2.headers['X-Custom-Processed']) # 'true' -``` - -### Authentication Hook - -```python -import requests -import hashlib -import time - -def custom_auth_hook(response, *args, **kwargs): - """Custom authentication logic.""" - if response.status_code == 401: - print("Authentication required - could trigger token refresh") - # In real implementation, might refresh token and retry request - return response - -def rate_limit_hook(response, *args, **kwargs): - """Handle rate limiting.""" - if response.status_code == 429: - retry_after = response.headers.get('Retry-After', '60') - print(f"Rate limited. Retry after {retry_after} seconds") - # Could implement automatic retry with backoff - return response - -# Combine authentication and rate limiting hooks -auth_hooks = { - 'response': [custom_auth_hook, rate_limit_hook] -} - -session = requests.Session() -session.hooks = auth_hooks - -response = session.get('https://httpbin.org/status/429') -# Prints: Rate limited. Retry after 60 seconds -``` - -### Request Modification Hook - -```python -import requests - -def add_user_agent_hook(response, *args, **kwargs): - """Add custom user agent information to response for logging.""" - original_ua = response.request.headers.get('User-Agent', 'Unknown') - print(f"Request made with User-Agent: {original_ua}") - - # Add metadata to response for later use - response.user_agent_info = { - 'original_ua': original_ua, - 'timestamp': time.time() - } - return response - -# Apply hook -response = requests.get('https://httpbin.org/user-agent', - hooks={'response': add_user_agent_hook}) - -print(response.user_agent_info) -``` - -### Error Handling Hook - -```python -import requests -import logging - -# Set up logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -def error_logging_hook(response, *args, **kwargs): - """Log errors and add context.""" - if response.status_code >= 400: - logger.error(f"HTTP Error {response.status_code}: {response.url}") - logger.error(f"Response: {response.text[:200]}...") - - # Add error context to response - response.error_logged = True - response.error_timestamp = time.time() - - return response - -def retry_header_hook(response, *args, **kwargs): - """Extract retry information from headers.""" - if response.status_code in [429, 503]: - retry_after = response.headers.get('Retry-After') - if retry_after: - response.retry_after_seconds = int(retry_after) - logger.info(f"Server suggests retry after {retry_after} seconds") - - return response - -# Error handling hook chain -error_hooks = { - 'response': [error_logging_hook, retry_header_hook] -} - -response = requests.get('https://httpbin.org/status/503', hooks=error_hooks) -print(f"Error logged: {getattr(response, 'error_logged', False)}") -``` - -### Performance Monitoring Hook - -```python -import requests -import time - -class PerformanceMonitor: - """Performance monitoring via hooks.""" - - def __init__(self): - self.stats = {} - - def response_hook(self, response, *args, **kwargs): - """Monitor response performance.""" - url = response.url - elapsed = response.elapsed.total_seconds() - status = response.status_code - size = len(response.content) - - # Track stats - if url not in self.stats: - self.stats[url] = [] - - self.stats[url].append({ - 'elapsed': elapsed, - 'status': status, - 'size': size, - 'timestamp': time.time() - }) - - print(f"PERF: {url} -> {status} ({elapsed:.3f}s, {size} bytes)") - return response - - def get_stats(self): - """Get performance statistics.""" - return self.stats - -# Use performance monitor -monitor = PerformanceMonitor() - -session = requests.Session() -session.hooks['response'].append(monitor.response_hook) - -# Make several requests -urls = [ - 'https://httpbin.org/get', - 'https://httpbin.org/json', - 'https://httpbin.org/html' -] - -for url in urls: - response = session.get(url) - -# View stats -stats = monitor.get_stats() -for url, measurements in stats.items(): - avg_time = sum(m['elapsed'] for m in measurements) / len(measurements) - print(f"Average time for {url}: {avg_time:.3f}s") -``` - -### Custom Response Processing Hook - -```python -import requests -import json - -def json_response_hook(response, *args, **kwargs): - """Automatically parse JSON responses.""" - content_type = response.headers.get('content-type', '') - - if 'application/json' in content_type: - try: - response.parsed_json = response.json() - response.json_parsed = True - except ValueError: - response.json_parsed = False - response.parsed_json = None - else: - response.json_parsed = False - response.parsed_json = None - - return response - -def xml_response_hook(response, *args, **kwargs): - """Handle XML responses.""" - content_type = response.headers.get('content-type', '') - - if 'xml' in content_type: - response.is_xml = True - # Could parse XML here with lxml or xml module - else: - response.is_xml = False - - return response - -# Content processing hooks -content_hooks = { - 'response': [json_response_hook, xml_response_hook] -} - -response = requests.get('https://httpbin.org/json', hooks=content_hooks) -print(f"JSON parsed: {response.json_parsed}") -print(f"Data: {response.parsed_json}") -``` - -### Hook Registration Patterns - -```python -import requests - -# Pattern 1: Direct function assignment -def my_hook(response, *args, **kwargs): - return response - -hooks = {'response': my_hook} - -# Pattern 2: List of functions -hooks = {'response': [hook1, hook2, hook3]} - -# Pattern 3: Adding to existing hooks -session = requests.Session() -session.hooks['response'].append(my_hook) - -# Pattern 4: Replacing all hooks -session.hooks['response'] = [my_hook] - -# Pattern 5: Using Request object -req = requests.Request('GET', 'https://httpbin.org/get', hooks=hooks) -prepared = req.prepare() - -with requests.Session() as session: - response = session.send(prepared) -``` - -## Hook Best Practices - -### Hook Function Signature - -```python -def my_hook(response, *args, **kwargs): - """ - Standard hook function signature. - - Parameters: - - response: Response object being processed - - *args: Additional positional arguments - - **kwargs: Additional keyword arguments - - Returns: - Modified or original Response object - """ - # Process response - return response -``` - -### Error Handling in Hooks - -```python -def safe_hook(response, *args, **kwargs): - """Hook with proper error handling.""" - try: - # Hook logic here - response.custom_data = process_response(response) - except Exception as e: - # Log error but don't break the request - print(f"Hook error: {e}") - response.hook_error = str(e) - - return response -``` - -### Performance Considerations - -```python -def efficient_hook(response, *args, **kwargs): - """Efficient hook implementation.""" - # Only process if needed - if should_process(response): - # Minimal processing - response.processed = True - - return response - -def should_process(response): - """Determine if processing is needed.""" - return response.headers.get('content-type') == 'application/json' -``` - -## Hook Limitations - -1. **Limited Events**: Only 'response' hook is currently supported -2. **No Request Hooks**: Cannot modify requests before sending -3. **Exception Handling**: Hook exceptions can break request processing -4. **Performance Impact**: Hooks add processing overhead -5. **State Management**: Hooks are stateless - use closures or classes for state - -## Advanced Hook Patterns - -### Hook Factory - -```python -def create_logging_hook(logger_name): - """Factory function to create logging hooks.""" - import logging - logger = logging.getLogger(logger_name) - - def logging_hook(response, *args, **kwargs): - logger.info(f"{response.request.method} {response.url} -> {response.status_code}") - return response - - return logging_hook - -# Create specialized hooks -api_hook = create_logging_hook('api_client') -web_hook = create_logging_hook('web_scraper') - -# Use different hooks for different purposes -api_session = requests.Session() -api_session.hooks['response'].append(api_hook) - -web_session = requests.Session() -web_session.hooks['response'].append(web_hook) -``` - -### Conditional Hooks - -```python -def conditional_hook(condition_func): - """Create a hook that only runs when condition is met.""" - def decorator(hook_func): - def wrapper(response, *args, **kwargs): - if condition_func(response): - return hook_func(response, *args, **kwargs) - return response - return wrapper - return decorator - -# Conditional hook decorators -@conditional_hook(lambda r: r.status_code >= 400) -def error_only_hook(response, *args, **kwargs): - print(f"Error response: {response.status_code}") - return response - -@conditional_hook(lambda r: 'json' in r.headers.get('content-type', '')) -def json_only_hook(response, *args, **kwargs): - print("Processing JSON response") - return response -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/http-methods.md b/.tessl/tiles/tessl/pypi-requests/docs/http-methods.md deleted file mode 100644 index 7e839da81..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/http-methods.md +++ /dev/null @@ -1,229 +0,0 @@ -# HTTP Methods - -Core HTTP method functions that provide the primary interface for making HTTP requests. These functions handle the most common HTTP verbs and automatically manage sessions internally. - -## Capabilities - -### Main Request Function - -The core request function that all other HTTP method functions use internally. Provides full control over HTTP method and request parameters. - -```python { .api } -def request(method: str, url: str, **kwargs) -> Response: - """ - Constructs and sends a Request. - - Parameters: - - method: HTTP method ('GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS') - - url: URL for the request - - params: dict, list of tuples or bytes for query string parameters - - data: dict, list of tuples, bytes, or file-like object for request body - - json: JSON serializable Python object for request body - - headers: dict of HTTP headers - - cookies: dict or CookieJar object - - files: dict of file-like objects for multipart encoding - - auth: auth tuple or auth handler instance - - timeout: float or (connect timeout, read timeout) tuple in seconds - - allow_redirects: bool to enable/disable redirects - - proxies: dict mapping protocol to proxy URL - - verify: bool or path to CA bundle for SSL verification - - stream: bool to download response content immediately - - cert: path to SSL client cert file or (cert, key) tuple - - Returns: - Response object - """ -``` - -### GET Requests - -Send HTTP GET requests to retrieve data from servers. - -```python { .api } -def get(url: str, params=None, **kwargs) -> Response: - """ - Sends a GET request. - - Parameters: - - url: URL for the request - - params: dict, list of tuples or bytes for query string parameters - - **kwargs: optional arguments that request() accepts - - Returns: - Response object - """ -``` - -Usage example: - -```python -import requests - -# Simple GET request -response = requests.get('https://api.github.com/users/octocat') - -# GET with query parameters -params = {'q': 'python', 'sort': 'stars'} -response = requests.get('https://api.github.com/search/repositories', params=params) - -# GET with headers and authentication -headers = {'User-Agent': 'MyApp/1.0'} -response = requests.get('https://api.example.com/data', - headers=headers, - auth=('username', 'password')) -``` - -### POST Requests - -Send HTTP POST requests to submit data to servers. - -```python { .api } -def post(url: str, data=None, json=None, **kwargs) -> Response: - """ - Sends a POST request. - - Parameters: - - url: URL for the request - - data: dict, list of tuples, bytes, or file-like object for request body - - json: JSON serializable Python object for request body - - **kwargs: optional arguments that request() accepts - - Returns: - Response object - """ -``` - -Usage example: - -```python -import requests - -# POST with form data -data = {'key1': 'value1', 'key2': 'value2'} -response = requests.post('https://httpbin.org/post', data=data) - -# POST with JSON data -json_data = {'user': 'john', 'age': 30} -response = requests.post('https://api.example.com/users', json=json_data) - -# POST with file upload -files = {'file': open('document.pdf', 'rb')} -response = requests.post('https://httpbin.org/post', files=files) -``` - -### PUT Requests - -Send HTTP PUT requests to create or update resources. - -```python { .api } -def put(url: str, data=None, **kwargs) -> Response: - """ - Sends a PUT request. - - Parameters: - - url: URL for the request - - data: dict, list of tuples, bytes, or file-like object for request body - - **kwargs: optional arguments that request() accepts - - Returns: - Response object - """ -``` - -### PATCH Requests - -Send HTTP PATCH requests to partially update resources. - -```python { .api } -def patch(url: str, data=None, **kwargs) -> Response: - """ - Sends a PATCH request. - - Parameters: - - url: URL for the request - - data: dict, list of tuples, bytes, or file-like object for request body - - **kwargs: optional arguments that request() accepts - - Returns: - Response object - """ -``` - -### DELETE Requests - -Send HTTP DELETE requests to remove resources. - -```python { .api } -def delete(url: str, **kwargs) -> Response: - """ - Sends a DELETE request. - - Parameters: - - url: URL for the request - - **kwargs: optional arguments that request() accepts - - Returns: - Response object - """ -``` - -### HEAD Requests - -Send HTTP HEAD requests to retrieve headers without response body. - -```python { .api } -def head(url: str, **kwargs) -> Response: - """ - Sends a HEAD request. - - Parameters: - - url: URL for the request - - **kwargs: optional arguments that request() accepts - Note: allow_redirects defaults to False for HEAD requests - - Returns: - Response object with empty content - """ -``` - -### OPTIONS Requests - -Send HTTP OPTIONS requests to determine allowed methods and capabilities. - -```python { .api } -def options(url: str, **kwargs) -> Response: - """ - Sends an OPTIONS request. - - Parameters: - - url: URL for the request - - **kwargs: optional arguments that request() accepts - - Returns: - Response object - """ -``` - -## Common Parameters - -All HTTP method functions accept these common optional parameters: - -- **headers**: `Dict[str, str]` - HTTP headers to send -- **auth**: `AuthType` - Authentication tuple or handler -- **timeout**: `Union[float, Tuple[float, float]]` - Request timeout in seconds -- **proxies**: `Dict[str, str]` - Proxy configuration -- **verify**: `Union[bool, str]` - SSL certificate verification -- **cert**: `Union[str, Tuple[str, str]]` - Client certificate -- **stream**: `bool` - Stream download response content -- **allow_redirects**: `bool` - Follow redirects (default True, except HEAD) -- **cookies**: `CookiesType` - Cookies to send - -## Error Handling - -All HTTP method functions can raise these exceptions: - -- **RequestException**: Base exception for all request-related errors -- **HTTPError**: 4xx or 5xx status codes (when raise_for_status() is called) -- **ConnectionError**: Connection-related errors -- **Timeout**: Request timeout errors -- **URLRequired**: Invalid or missing URL \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/index.md b/.tessl/tiles/tessl/pypi-requests/docs/index.md deleted file mode 100644 index b4e0bece4..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/index.md +++ /dev/null @@ -1,262 +0,0 @@ -# Requests - -Python HTTP for Humans - an elegant and simple HTTP library that abstracts the complexities of making HTTP requests behind a beautiful, simple API. Requests allows you to send HTTP/1.1 requests extremely easily with features like automatic content decoding, connection pooling, cookie persistence, authentication, and comprehensive SSL verification. - -## Package Information - -- **Package Name**: requests -- **Language**: Python -- **Installation**: `pip install requests` -- **Version**: 2.32.4 - -## Core Imports - -```python -import requests -``` - -Individual components: - -```python -from requests import Session, Request, PreparedRequest, Response, codes -from requests.auth import HTTPBasicAuth, HTTPDigestAuth, HTTPProxyAuth, AuthBase -from requests.exceptions import ( - RequestException, HTTPError, ConnectionError, Timeout, ConnectTimeout, - ReadTimeout, URLRequired, TooManyRedirects, SSLError, JSONDecodeError -) -from requests.adapters import HTTPAdapter, BaseAdapter -from requests.structures import CaseInsensitiveDict, LookupDict -from requests.cookies import RequestsCookieJar -``` - -## Basic Usage - -```python -import requests - -# Simple GET request -response = requests.get('https://api.github.com/user', auth=('user', 'pass')) -print(response.status_code) -print(response.json()) - -# POST request with JSON data -data = {'key': 'value'} -response = requests.post('https://httpbin.org/post', json=data) - -# Using sessions for persistent settings -session = requests.Session() -session.auth = ('user', 'pass') -session.headers.update({'Custom-Header': 'value'}) - -response = session.get('https://api.example.com/data') -``` - -## Architecture - -Requests is built around several key components: - -- **HTTP Methods**: Simple functions (get, post, put, etc.) that create and send requests -- **Session**: Persistent configuration and connection pooling across requests -- **Request/PreparedRequest**: Request objects representing HTTP requests before sending -- **Response**: Response objects containing server responses with convenient access methods -- **Authentication**: Pluggable authentication handlers for various auth schemes -- **Adapters**: Transport adapters that handle the actual HTTP communication -- **Exceptions**: Comprehensive exception hierarchy for different error conditions - -## Capabilities - -### HTTP Methods - -Core HTTP method functions for making requests with various verbs. These are the primary interface most users interact with. - -```python { .api } -def request(method: str, url: str, **kwargs) -> Response: ... -def get(url: str, params=None, **kwargs) -> Response: ... -def post(url: str, data=None, json=None, **kwargs) -> Response: ... -def put(url: str, data=None, **kwargs) -> Response: ... -def patch(url: str, data=None, **kwargs) -> Response: ... -def delete(url: str, **kwargs) -> Response: ... -def head(url: str, **kwargs) -> Response: ... -def options(url: str, **kwargs) -> Response: ... -``` - -[HTTP Methods](./http-methods.md) - -### Sessions - -Session objects provide persistent configuration, connection pooling, and cookie persistence across multiple requests. - -```python { .api } -class Session: - def __init__(self): ... - def request(self, method: str, url: str, **kwargs) -> Response: ... - def get(self, url: str, **kwargs) -> Response: ... - def post(self, url: str, **kwargs) -> Response: ... - def close(self): ... - -def session() -> Session: ... -``` - -[Sessions](./sessions.md) - -### Request and Response Objects - -Core objects representing HTTP requests and responses with full control over request preparation and response handling. - -```python { .api } -class Request: - def __init__(self, method=None, url=None, headers=None, files=None, - data=None, params=None, auth=None, cookies=None, - hooks=None, json=None): ... - def prepare(self) -> PreparedRequest: ... - -class Response: - content: bytes - text: str - status_code: int - headers: dict - cookies: dict - url: str - def json(self, **kwargs) -> dict: ... - def raise_for_status(self): ... -``` - -[Request and Response Objects](./models.md) - -### Authentication - -Authentication handlers for various HTTP authentication schemes including Basic, Digest, and custom authentication. - -```python { .api } -class HTTPBasicAuth: - def __init__(self, username: str, password: str): ... - -class HTTPDigestAuth: - def __init__(self, username: str, password: str): ... - -class HTTPProxyAuth(HTTPBasicAuth): ... -``` - -[Authentication](./authentication.md) - -### Exception Handling - -Comprehensive exception hierarchy for handling various error conditions that can occur during HTTP requests. - -```python { .api } -class RequestException(IOError): ... -class HTTPError(RequestException): ... -class ConnectionError(RequestException): ... -class Timeout(RequestException): ... -class ConnectTimeout(ConnectionError, Timeout): ... -class ReadTimeout(Timeout): ... -class URLRequired(RequestException): ... -class TooManyRedirects(RequestException): ... -class SSLError(ConnectionError): ... -``` - -[Exception Handling](./exceptions.md) - -### Status Codes - -Convenient access to HTTP status codes through named constants and lookup functionality. - -```python { .api } -codes: dict # Lookup dict for status codes -# Usage: codes.ok == 200, codes['not_found'] == 404 -``` - -[Status Codes](./status-codes.md) - -### Cookie Handling - -Cookie management functionality providing a dict-like interface for handling HTTP cookies with compatibility for both client and server-side cookie operations. - -```python { .api } -class RequestsCookieJar: - def __init__(self, policy=None): ... - def get(self, name: str, default=None, domain=None, path=None) -> str: ... - def set(self, name: str, value: str, **kwargs): ... - def __getitem__(self, name: str) -> str: ... - def __setitem__(self, name: str, value: str): ... - -def cookiejar_from_dict(cookie_dict: dict, cookiejar=None, overwrite=True): ... -def merge_cookies(cookiejar, cookies): ... -``` - -[Cookie Handling](./cookies.md) - -### Transport Adapters - -Transport adapters handle the actual HTTP communication, providing the interface between requests and underlying HTTP libraries. - -```python { .api } -class BaseAdapter: - def send(self, request, stream=False, timeout=None, verify=True, - cert=None, proxies=None) -> Response: ... - def close(self): ... - -class HTTPAdapter(BaseAdapter): - def __init__(self, pool_connections=10, pool_maxsize=10, max_retries=0, - pool_block=False): ... - def send(self, request, **kwargs) -> Response: ... - def mount(self, prefix: str, adapter): ... -``` - -[Transport Adapters](./adapters.md) - -### Data Structures - -Data structure classes that provide enhanced dictionary interfaces with special behaviors for HTTP-related operations. - -```python { .api } -class CaseInsensitiveDict(dict): - def __init__(self, data=None, **kwargs): ... - def __getitem__(self, key): ... - def __setitem__(self, key, value): ... - def copy(self) -> 'CaseInsensitiveDict': ... - -class LookupDict(dict): - def __init__(self, name=None): ... - def __getitem__(self, key): ... - def get(self, key, default=None): ... -``` - -[Data Structures](./structures.md) - -### Event Hooks - -Event hook system that allows custom functions to be called at specific points during request processing. - -```python { .api } -HOOKS: list[str] # ['response'] - -def default_hooks() -> dict: ... -def dispatch_hook(key: str, hooks: dict, hook_data, **kwargs): ... -``` - -[Event Hooks](./hooks.md) - -## Types - -```python { .api } -from typing import Dict, Optional, Union, Any, List, Tuple, IO -from datetime import timedelta - -# Common type aliases -JSONType = Union[Dict[str, Any], List[Any], str, int, float, bool, None] -AuthType = Union[Tuple[str, str], HTTPBasicAuth, HTTPDigestAuth, HTTPProxyAuth, AuthBase] -CookiesType = Union[Dict[str, str], RequestsCookieJar] -HeadersType = Union[Dict[str, str], CaseInsensitiveDict] -ParamsType = Union[Dict[str, str], List[Tuple[str, str]], bytes] -DataType = Union[Dict[str, Any], List[Tuple[str, str]], str, bytes, IO] -FilesType = Dict[str, Union[str, bytes, IO, Tuple[str, Union[str, bytes, IO]], - Tuple[str, Union[str, bytes, IO], str], - Tuple[str, Union[str, bytes, IO], str, Dict[str, str]]]] -ProxiesType = Dict[str, str] -TimeoutType = Union[float, Tuple[float, float]] -VerifyType = Union[bool, str] -CertType = Union[str, Tuple[str, str]] -HooksType = Dict[str, List[callable]] -AdapterType = BaseAdapter -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/models.md b/.tessl/tiles/tessl/pypi-requests/docs/models.md deleted file mode 100644 index d1deeef14..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/models.md +++ /dev/null @@ -1,378 +0,0 @@ -# Request and Response Objects - -Core objects that represent HTTP requests and responses, providing full control over request preparation and response handling. These objects form the foundation of the requests library's functionality. - -## Capabilities - -### Request Class - -User-created Request objects that represent HTTP requests before they are sent. - -```python { .api } -class Request: - """ - A user-created Request object. - - Used to prepare a PreparedRequest, which is sent to the server. - """ - - def __init__(self, method=None, url=None, headers=None, files=None, - data=None, params=None, auth=None, cookies=None, - hooks=None, json=None): - """ - Initialize a Request object. - - Parameters: - - method: HTTP method to use ('GET', 'POST', etc.) - - url: URL to send the request to - - headers: dict of headers to send - - files: dict of {filename: fileobject} for multipart upload - - data: request body data (dict, list of tuples, bytes, or file-like) - - params: URL parameters to append (dict or list of tuples) - - auth: authentication tuple or handler - - cookies: cookies to send (dict or CookieJar) - - hooks: event hooks dict - - json: JSON serializable object for request body - """ - - def prepare(self) -> 'PreparedRequest': - """ - Prepare the request for sending. - - Returns: - PreparedRequest object ready to send - """ -``` - -### PreparedRequest Class - -Fully prepared request objects containing the exact bytes that will be sent to the server. - -```python { .api } -class PreparedRequest: - """ - The fully mutable PreparedRequest object. - - Contains the exact bytes that will be sent to the server. - Should not be instantiated manually. - - Attributes: - - method: HTTP method (str) - - url: Full URL (str) - - headers: Request headers (CaseInsensitiveDict) - - body: Request body (bytes or str or None) - - hooks: Event hooks (dict) - - _cookies: CookieJar for cookie header generation - - _body_position: Position marker for rewindable request bodies - """ - - def __init__(self): - """Initialize a PreparedRequest object.""" - - def prepare(self, method=None, url=None, files=None, data=None, - headers=None, params=None, auth=None, cookies=None, - hooks=None, json=None): - """ - Prepare all aspects of the request. - - Parameters: Same as Request.__init__ - """ - - def prepare_method(self, method: str): - """Prepare the HTTP method.""" - - def prepare_url(self, url: str, params): - """Prepare the URL with parameters.""" - - def prepare_headers(self, headers): - """Prepare the headers.""" - - def prepare_cookies(self, cookies): - """Prepare the cookies.""" - - def prepare_body(self, data, files, json=None): - """Prepare the request body.""" - - def prepare_auth(self, auth, url=''): - """Prepare authentication.""" - - def prepare_content_length(self, body): - """Prepare Content-Length header.""" - - def prepare_hooks(self, hooks): - """Prepare event hooks.""" - - def copy(self) -> 'PreparedRequest': - """Create a copy of the PreparedRequest.""" -``` - -### Response Class - -Response objects containing server responses to HTTP requests. - -```python { .api } -class Response: - """ - The Response object contains a server's response to an HTTP request. - """ - - # Response attributes - content: bytes # Response content as bytes - text: str # Response content as text - encoding: str # Response encoding - status_code: int # HTTP status code - headers: 'CaseInsensitiveDict' # Response headers - cookies: 'RequestsCookieJar' # Response cookies - url: str # Final URL location of response - history: list['Response'] # List of Response objects (redirects) - reason: str # Textual reason of response (e.g., 'OK', 'Not Found') - request: 'PreparedRequest' # PreparedRequest that generated this response - elapsed: 'timedelta' # Time elapsed between request and response - raw: object # Raw response object (urllib3.HTTPResponse) - - def __init__(self): - """Initialize a Response object.""" - - def __enter__(self): - """Context manager entry.""" - - def __exit__(self, *args): - """Context manager exit.""" - - def __bool__(self) -> bool: - """Boolean evaluation based on status code.""" - - def __nonzero__(self) -> bool: - """Boolean evaluation for Python 2 compatibility.""" - - def __iter__(self): - """Iterate over response content in chunks.""" - - @property - def ok(self) -> bool: - """True if status code is less than 400.""" - - @property - def is_redirect(self) -> bool: - """True if response is a redirect.""" - - @property - def is_permanent_redirect(self) -> bool: - """True if response is a permanent redirect.""" - - @property - def next(self): - """Returns parsed header links if present.""" - - @property - def apparent_encoding(self) -> str: - """Apparent encoding of response content.""" - - def iter_content(self, chunk_size: int = 1, decode_unicode: bool = False): - """ - Iterate over response data in chunks. - - Parameters: - - chunk_size: Size of chunks to read - - decode_unicode: Whether to decode content to unicode - - Yields: - Chunks of response content - """ - - def iter_lines(self, chunk_size: int = 512, decode_unicode: bool = False, - delimiter=None): - """ - Iterate over response lines. - - Parameters: - - chunk_size: Size of chunks to read - - decode_unicode: Whether to decode content to unicode - - delimiter: Line delimiter - - Yields: - Lines from response content - """ - - def json(self, **kwargs) -> Union[dict, list]: - """ - Parse response content as JSON. - - Parameters: - - **kwargs: Arguments passed to json.loads() - - Returns: - Parsed JSON data - - Raises: - JSONDecodeError: If response is not valid JSON - """ - - @property - def links(self) -> dict: - """Returns parsed header links.""" - - def raise_for_status(self): - """ - Raise HTTPError for bad responses (4xx or 5xx status codes). - - Raises: - HTTPError: If status code indicates an error - """ - - def close(self): - """Release the connection back to the pool.""" -``` - -## Usage Examples - -### Basic Request Creation - -```python -import requests - -# Create a request object -req = requests.Request('GET', 'https://api.github.com/user', - auth=('username', 'password')) - -# Prepare the request -prepared = req.prepare() - -# Send the prepared request -with requests.Session() as s: - response = s.send(prepared) - print(response.status_code) -``` - -### Working with Response Objects - -```python -import requests - -response = requests.get('https://api.github.com/users/octocat') - -# Access response properties -print(f"Status: {response.status_code}") -print(f"Headers: {response.headers}") -print(f"URL: {response.url}") -print(f"Encoding: {response.encoding}") - -# Check if request was successful -if response.ok: - print("Request successful") - -# Parse JSON response -try: - data = response.json() - print(f"User: {data['login']}") -except requests.exceptions.JSONDecodeError: - print("Response is not valid JSON") - -# Check for errors -try: - response.raise_for_status() -except requests.exceptions.HTTPError as e: - print(f"HTTP Error: {e}") -``` - -### Streaming Large Responses - -```python -import requests - -# Stream large file download -url = 'https://example.com/large-file.zip' -response = requests.get(url, stream=True) - -with open('large-file.zip', 'wb') as f: - for chunk in response.iter_content(chunk_size=8192): - f.write(chunk) - -# Stream and process lines -response = requests.get('https://example.com/large-text-file.txt', stream=True) -for line in response.iter_lines(decode_unicode=True): - print(line) - -response.close() # Important to close streamed responses -``` - -### Response Context Manager - -```python -import requests - -# Use response as context manager for automatic cleanup -with requests.get('https://example.com/data.json', stream=True) as response: - response.raise_for_status() - data = response.json() - # Process data... -# Response is automatically closed -``` - -### Advanced Request Preparation - -```python -import requests - -# Create and customize a request -req = requests.Request( - method='POST', - url='https://api.example.com/data', - json={'key': 'value'}, - headers={'User-Agent': 'MyApp/1.0'}, - auth=('user', 'pass') -) - -# Prepare and inspect before sending -prepared = req.prepare() -print(f"Method: {prepared.method}") -print(f"URL: {prepared.url}") -print(f"Headers: {prepared.headers}") -print(f"Body: {prepared.body}") - -# Send the prepared request -with requests.Session() as s: - response = s.send(prepared, timeout=30) -``` - -### Response History and Redirects - -```python -import requests - -response = requests.get('https://github.com') - -# Check redirect history -if response.history: - print("Request was redirected") - for resp in response.history: - print(f"Redirect from: {resp.url} -> {resp.status_code}") - print(f"Final URL: {response.url}") - -# Check redirect type -if response.is_redirect: - print("Response is a redirect") -if response.is_permanent_redirect: - print("Response is a permanent redirect") -``` - -## Constants - -```python { .api } -# Redirect status codes -REDIRECT_STATI: tuple # (301, 302, 303, 307, 308) - -# Default limits -DEFAULT_REDIRECT_LIMIT: int # 30 -CONTENT_CHUNK_SIZE: int # 10 * 1024 -ITER_CHUNK_SIZE: int # 512 -``` - -## Request/Response Lifecycle - -1. **Request Creation**: User creates a Request object with parameters -2. **Request Preparation**: Request is converted to PreparedRequest with exact bytes -3. **Request Sending**: PreparedRequest is sent via adapter (usually HTTPAdapter) -4. **Response Creation**: Server response is converted to Response object -5. **Response Processing**: User accesses response data, headers, status, etc. -6. **Cleanup**: Response connection is returned to pool or closed \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/sessions.md b/.tessl/tiles/tessl/pypi-requests/docs/sessions.md deleted file mode 100644 index 8b9f5d87d..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/sessions.md +++ /dev/null @@ -1,259 +0,0 @@ -# Sessions - -Session objects provide a way to persist certain parameters across requests, enabling connection pooling, cookie persistence, and default configuration. Sessions are essential for maintaining state across multiple requests and improving performance through connection reuse. - -## Capabilities - -### Session Class - -The Session class provides persistent configuration and connection pooling across multiple requests. - -```python { .api } -class Session: - """ - Session object for persisting settings across requests. - - Attributes: - - headers: Default headers for all requests - - cookies: Cookie jar for persistent cookies - - auth: Default authentication - - proxies: Default proxy configuration - - hooks: Event hooks - - params: Default URL parameters - - verify: Default SSL verification setting - - cert: Default client certificate - - max_redirects: Maximum number of redirects to follow - - trust_env: Whether to trust environment variables for configuration - - stream: Default streaming setting - - adapters: OrderedDict of mounted transport adapters - """ - - def __init__(self): - """Initialize a new Session.""" - - def __enter__(self): - """Context manager entry.""" - - def __exit__(self, *args): - """Context manager exit.""" - - def prepare_request(self, request: Request) -> PreparedRequest: - """ - Prepare a Request object for sending. - - Parameters: - - request: Request object to prepare - - Returns: - PreparedRequest object ready to send - """ - - def request(self, method: str, url: str, **kwargs) -> Response: - """ - Send a request using the session configuration. - - Parameters: - - method: HTTP method - - url: URL for the request - - **kwargs: additional request parameters - - Returns: - Response object - """ - - def get(self, url: str, **kwargs) -> Response: - """Send a GET request using session configuration.""" - - def options(self, url: str, **kwargs) -> Response: - """Send an OPTIONS request using session configuration.""" - - def head(self, url: str, **kwargs) -> Response: - """Send a HEAD request using session configuration.""" - - def post(self, url: str, data=None, json=None, **kwargs) -> Response: - """Send a POST request using session configuration.""" - - def put(self, url: str, data=None, **kwargs) -> Response: - """Send a PUT request using session configuration.""" - - def patch(self, url: str, data=None, **kwargs) -> Response: - """Send a PATCH request using session configuration.""" - - def delete(self, url: str, **kwargs) -> Response: - """Send a DELETE request using session configuration.""" - - def send(self, request: PreparedRequest, **kwargs) -> Response: - """ - Send a prepared request. - - Parameters: - - request: PreparedRequest object - - **kwargs: additional sending parameters like timeout, verify, etc. - - Returns: - Response object - """ - - def merge_environment_settings(self, url: str, proxies: dict, stream: bool, - verify: Union[bool, str], cert: Union[str, tuple]): - """ - Merge environment settings with session settings. - - Parameters: - - url: Request URL - - proxies: Proxy configuration - - stream: Stream setting - - verify: SSL verification setting - - cert: Client certificate setting - - Returns: - Dict of merged settings - """ - - def get_adapter(self, url: str): - """ - Get the appropriate adapter for the given URL. - - Parameters: - - url: URL to get adapter for - - Returns: - Adapter instance - """ - - def close(self): - """Close the session and clean up resources.""" - - def mount(self, prefix: str, adapter): - """ - Register an adapter for a URL prefix. - - Parameters: - - prefix: URL prefix (e.g., 'https://') - - adapter: Adapter instance to mount - """ -``` - -### Session Factory Function - -Convenience function to create a new session. - -```python { .api } -def session() -> Session: - """ - Create and return a new Session object. - - Returns: - New Session instance - """ -``` - -## Usage Examples - -### Basic Session Usage - -```python -import requests - -# Create a session -s = requests.Session() - -# Set default headers and authentication -s.headers.update({'User-Agent': 'MyApp/1.0'}) -s.auth = ('username', 'password') - -# Make requests using the session -response1 = s.get('https://api.example.com/data') -response2 = s.post('https://api.example.com/update', json={'key': 'value'}) - -# Cookies are automatically persisted -response3 = s.get('https://api.example.com/profile') # Cookies from previous requests are sent - -# Close the session -s.close() -``` - -### Session as Context Manager - -```python -import requests - -# Use session as a context manager for automatic cleanup -with requests.Session() as s: - s.headers.update({'Authorization': 'Bearer token123'}) - - response = s.get('https://api.example.com/protected') - data = response.json() - - # Process data... - -# Session is automatically closed when exiting the context -``` - -### Persistent Configuration - -```python -import requests - -s = requests.Session() - -# Set persistent configuration -s.headers.update({ - 'User-Agent': 'MyApp/2.0', - 'Accept': 'application/json' -}) -s.auth = ('api_user', 'api_pass') -s.proxies = {'http': 'http://proxy.example.com:8080'} -s.verify = '/path/to/ca-bundle.crt' - -# All requests will use these settings by default -response1 = s.get('https://api.example.com/users') -response2 = s.post('https://api.example.com/data', json={'name': 'John'}) - -# Override settings for specific requests -response3 = s.get('https://other-api.com/data', auth=None, verify=False) -``` - -### Connection Pooling Benefits - -```python -import requests - -# Without session - new connection for each request -for i in range(10): - requests.get('https://api.example.com/data/' + str(i)) # 10 separate connections - -# With session - connection pooling -with requests.Session() as s: - for i in range(10): - s.get('https://api.example.com/data/' + str(i)) # Reuses connections -``` - -## Session Attributes - -Sessions have several configurable attributes: - -- **headers**: `CaseInsensitiveDict` - Default headers for all requests -- **cookies**: `RequestsCookieJar` - Persistent cookie storage -- **auth**: `AuthType` - Default authentication handler -- **proxies**: `Dict[str, str]` - Default proxy configuration -- **hooks**: `Dict[str, List[Callable]]` - Event hooks for request/response processing -- **params**: `Dict[str, str]` - Default URL parameters -- **verify**: `Union[bool, str]` - Default SSL certificate verification -- **cert**: `Union[str, Tuple[str, str]]` - Default client certificate -- **max_redirects**: `int` - Maximum redirects to follow (default: 30) -- **trust_env**: `bool` - Trust environment variables for proxy config -- **stream**: `bool` - Default streaming behavior - -## Session Methods vs Module Functions - -Session methods provide the same interface as module-level functions but with persistent configuration: - -| Module Function | Session Method | Benefit | -|----------------|----------------|---------| -| `requests.get()` | `session.get()` | Persistent headers, cookies, auth | -| `requests.post()` | `session.post()` | Connection pooling | -| `requests.put()` | `session.put()` | Proxy configuration | -| `requests.delete()` | `session.delete()` | SSL settings | - -Session methods inherit all session configuration but can be overridden with method-specific parameters. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/status-codes.md b/.tessl/tiles/tessl/pypi-requests/docs/status-codes.md deleted file mode 100644 index e096abb3a..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/status-codes.md +++ /dev/null @@ -1,373 +0,0 @@ -# Status Codes - -Convenient access to HTTP status codes through named constants and lookup functionality. The requests library provides a `codes` object that maps common status code names to their numerical values. - -## Capabilities - -### Status Code Lookup - -The `codes` object provides multiple ways to access status codes by name. - -```python { .api } -codes: LookupDict -# A lookup dictionary that maps status code names to their numerical values -# Supports both attribute and dictionary-style access -# Multiple names can map to the same status code -# Both upper and lowercase names are supported -``` - -## Usage Examples - -### Basic Status Code Access - -```python -import requests - -# Access status codes by name -print(requests.codes.ok) # 200 -print(requests.codes.not_found) # 404 -print(requests.codes.internal_server_error) # 500 - -# Dictionary-style access -print(requests.codes['ok']) # 200 -print(requests.codes['not_found']) # 404 -print(requests.codes['server_error']) # 500 - -# Case-insensitive access -print(requests.codes.OK) # 200 -print(requests.codes.Ok) # 200 -print(requests.codes.okay) # 200 -``` - -### Status Code Checking - -```python -import requests - -response = requests.get('https://httpbin.org/status/200') - -# Check specific status codes -if response.status_code == requests.codes.ok: - print("Request successful") - -if response.status_code == requests.codes.not_found: - print("Resource not found") - -# Check for success range -if 200 <= response.status_code < 300: - print("Success") - -# Using response.ok property (shorthand for status < 400) -if response.ok: - print("Request successful") -``` - -### Status Code Categories - -```python -import requests - -def categorize_status(status_code): - """Categorize HTTP status codes.""" - if 100 <= status_code < 200: - return "Informational" - elif 200 <= status_code < 300: - return "Success" - elif 300 <= status_code < 400: - return "Redirection" - elif 400 <= status_code < 500: - return "Client Error" - elif 500 <= status_code < 600: - return "Server Error" - else: - return "Unknown" - -response = requests.get('https://httpbin.org/status/404') -category = categorize_status(response.status_code) -print(f"Status {response.status_code} is a {category}") -``` - -### Common Status Code Names - -```python -import requests - -# Informational (1xx) -codes.continue_ # 100 -codes.switching_protocols # 101 -codes.processing # 102 - -# Success (2xx) -codes.ok # 200 -codes.okay # 200 (alias) -codes.all_ok # 200 (alias) -codes.all_good # 200 (alias) -codes['\\o/'] # 200 (fun alias) -codes['✓'] # 200 (checkmark alias) -codes.created # 201 -codes.accepted # 202 -codes.no_content # 204 - -# Redirection (3xx) -codes.multiple_choices # 300 -codes.moved_permanently # 301 -codes.moved # 301 (alias) -codes.found # 302 -codes.see_other # 303 -codes.not_modified # 304 -codes.temporary_redirect # 307 -codes.permanent_redirect # 308 - -# Client Error (4xx) -codes.bad_request # 400 -codes.unauthorized # 401 -codes.forbidden # 403 -codes.not_found # 404 -codes.method_not_allowed # 405 -codes.not_acceptable # 406 -codes.request_timeout # 408 -codes.conflict # 409 -codes.gone # 410 -codes.precondition_failed # 412 -codes.request_entity_too_large # 413 -codes.unsupported_media_type # 415 -codes.too_many_requests # 429 - -# Server Error (5xx) -codes.internal_server_error # 500 -codes.server_error # 500 (alias) -codes.not_implemented # 501 -codes.bad_gateway # 502 -codes.service_unavailable # 503 -codes.gateway_timeout # 504 -``` - -### Response Status Checking with Codes - -```python -import requests - -def handle_response(response): - """Handle response based on status code.""" - - if response.status_code == requests.codes.ok: - return response.json() - - elif response.status_code == requests.codes.created: - print("Resource created successfully") - return response.json() - - elif response.status_code == requests.codes.no_content: - print("Operation successful, no content returned") - return None - - elif response.status_code == requests.codes.not_modified: - print("Resource not modified, using cached version") - return None - - elif response.status_code == requests.codes.bad_request: - print("Bad request - check your parameters") - return None - - elif response.status_code == requests.codes.unauthorized: - print("Authentication required") - return None - - elif response.status_code == requests.codes.forbidden: - print("Access forbidden") - return None - - elif response.status_code == requests.codes.not_found: - print("Resource not found") - return None - - elif response.status_code == requests.codes.too_many_requests: - print("Rate limit exceeded") - return None - - elif response.status_code == requests.codes.internal_server_error: - print("Internal server error") - return None - - elif response.status_code == requests.codes.service_unavailable: - print("Service temporarily unavailable") - return None - - else: - print(f"Unexpected status code: {response.status_code}") - return None - -# Usage -response = requests.get('https://api.example.com/data') -data = handle_response(response) -``` - -### Status Code Comparison - -```python -import requests - -response = requests.get('https://httpbin.org/status/200') - -# Multiple ways to check for success -if response.status_code == 200: - print("Success (numeric)") - -if response.status_code == requests.codes.ok: - print("Success (using codes)") - -if response.ok: - print("Success (using ok property)") - -# Check for specific error conditions -if response.status_code in [requests.codes.unauthorized, requests.codes.forbidden]: - print("Authentication or authorization error") - -# Check ranges -if 400 <= response.status_code < 500: - print("Client error") -elif 500 <= response.status_code < 600: - print("Server error") -``` - -### Custom Status Code Handling - -```python -import requests - -class StatusHandler: - """Custom status code handler.""" - - @staticmethod - def is_success(status_code): - """Check if status code indicates success.""" - return 200 <= status_code < 300 - - @staticmethod - def is_redirect(status_code): - """Check if status code indicates redirect.""" - return status_code in [ - requests.codes.moved_permanently, - requests.codes.found, - requests.codes.see_other, - requests.codes.temporary_redirect, - requests.codes.permanent_redirect - ] - - @staticmethod - def is_client_error(status_code): - """Check if status code indicates client error.""" - return 400 <= status_code < 500 - - @staticmethod - def is_server_error(status_code): - """Check if status code indicates server error.""" - return 500 <= status_code < 600 - - @staticmethod - def should_retry(status_code): - """Determine if request should be retried based on status.""" - retry_codes = [ - requests.codes.request_timeout, - requests.codes.too_many_requests, - requests.codes.internal_server_error, - requests.codes.bad_gateway, - requests.codes.service_unavailable, - requests.codes.gateway_timeout - ] - return status_code in retry_codes - -# Usage -response = requests.get('https://api.example.com/data') -handler = StatusHandler() - -if handler.is_success(response.status_code): - data = response.json() -elif handler.should_retry(response.status_code): - print("Should retry this request") -elif handler.is_client_error(response.status_code): - print("Client error - check request") -else: - print("Unexpected status code") -``` - -### Status Code Logging - -```python -import requests -import logging - -# Set up logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -def logged_request(url): - """Make request with detailed status logging.""" - try: - response = requests.get(url) - - # Log status information - if response.status_code == requests.codes.ok: - logger.info(f"Request successful: {response.status_code}") - elif 300 <= response.status_code < 400: - logger.info(f"Redirect: {response.status_code} -> {response.headers.get('Location', 'Unknown')}") - elif 400 <= response.status_code < 500: - logger.warning(f"Client error: {response.status_code} - {response.reason}") - elif 500 <= response.status_code < 600: - logger.error(f"Server error: {response.status_code} - {response.reason}") - else: - logger.info(f"Unexpected status: {response.status_code}") - - return response - - except requests.exceptions.RequestException as e: - logger.error(f"Request failed: {e}") - raise - -# Usage -response = logged_request('https://httpbin.org/status/404') -``` - -## Complete Status Code Reference - -The `codes` object includes mappings for all standard HTTP status codes with multiple name variations: - -### Informational (1xx) -- `continue` (100) -- `switching_protocols` (101) -- `processing` (102) - -### Success (2xx) -- `ok`, `okay`, `all_ok`, `all_good`, `\\o/`, `✓` (200) -- `created` (201) -- `accepted` (202) -- `no_content` (204) -- `partial_content` (206) - -### Redirection (3xx) -- `moved_permanently`, `moved` (301) -- `found` (302) -- `see_other`, `other` (303) -- `not_modified` (304) -- `temporary_redirect` (307) -- `permanent_redirect` (308) - -### Client Error (4xx) -- `bad_request` (400) -- `unauthorized` (401) -- `forbidden` (403) -- `not_found` (404) -- `method_not_allowed` (405) -- `request_timeout` (408) -- `conflict` (409) -- `gone` (410) -- `too_many_requests` (429) - -### Server Error (5xx) -- `internal_server_error`, `server_error` (500) -- `not_implemented` (501) -- `bad_gateway` (502) -- `service_unavailable` (503) -- `gateway_timeout` (504) - -Many codes have multiple aliases - both the formal name and common variations are supported. \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/docs/structures.md b/.tessl/tiles/tessl/pypi-requests/docs/structures.md deleted file mode 100644 index b8afbac84..000000000 --- a/.tessl/tiles/tessl/pypi-requests/docs/structures.md +++ /dev/null @@ -1,457 +0,0 @@ -# Data Structures - -Data structure classes that provide enhanced dictionary interfaces with special behaviors for HTTP-related operations. These structures are used throughout the requests library for headers, status codes, and general data handling. - -## Capabilities - -### CaseInsensitiveDict Class - -Dictionary with case-insensitive key lookup, primarily used for HTTP headers. - -```python { .api } -class CaseInsensitiveDict(dict): - """ - A case-insensitive dictionary implementation. - - Used for HTTP headers where keys should be treated case-insensitively - according to HTTP specification. - """ - - def __init__(self, data=None, **kwargs): - """ - Initialize CaseInsensitiveDict. - - Parameters: - - data: Initial data (dict, list of tuples, or another CaseInsensitiveDict) - - **kwargs: Additional key-value pairs - """ - - def __setitem__(self, key, value): - """Set value with case-insensitive key.""" - - def __getitem__(self, key): - """Get value with case-insensitive key lookup.""" - - def __delitem__(self, key): - """Delete item with case-insensitive key lookup.""" - - def __iter__(self): - """Iterate over original-case keys.""" - - def __len__(self) -> int: - """Get number of items.""" - - def __eq__(self, other) -> bool: - """Compare with another mapping (case-insensitive).""" - - def __repr__(self) -> str: - """String representation.""" - - def lower_items(self): - """ - Iterate over (lowercase_key, value) pairs. - - Yields: - Tuples of (lowercase_key, value) - """ - - def copy(self) -> 'CaseInsensitiveDict': - """Create a copy of the dictionary.""" -``` - -### LookupDict Class - -Dictionary subclass with attribute-style access, used for status codes. - -```python { .api } -class LookupDict(dict): - """ - A dictionary that supports both item and attribute access. - - Used for status codes to enable both codes['ok'] and codes.ok syntax. - """ - - def __init__(self, name=None): - """ - Initialize LookupDict. - - Parameters: - - name: Optional name for the lookup dict - """ - - def __getitem__(self, key): - """Get item with fallback to __dict__ lookup.""" - - def get(self, key, default=None): - """ - Get value with default. - - Parameters: - - key: Key to look up - - default: Default value if key not found - - Returns: - Value or default - """ - - def __repr__(self) -> str: - """String representation.""" -``` - -## Usage Examples - -### CaseInsensitiveDict Usage - -```python -import requests -from requests.structures import CaseInsensitiveDict - -# CaseInsensitiveDict is used for response headers -response = requests.get('https://httpbin.org/get') -headers = response.headers - -# All these access the same header (case-insensitive) -print(headers['Content-Type']) # Works -print(headers['content-type']) # Works -print(headers['CONTENT-TYPE']) # Works -print(headers['Content-type']) # Works - -# Check header existence (case-insensitive) -print('content-type' in headers) # True -print('Content-Type' in headers) # True - -# Iterate over headers (preserves original case) -for name, value in headers.items(): - print(f"{name}: {value}") -``` - -### Manual CaseInsensitiveDict Creation - -```python -from requests.structures import CaseInsensitiveDict - -# Create from dict -headers = CaseInsensitiveDict({ - 'Content-Type': 'application/json', - 'Authorization': 'Bearer token123', - 'User-Agent': 'MyApp/1.0' -}) - -# Access with any case -print(headers['content-type']) # 'application/json' -print(headers['AUTHORIZATION']) # 'Bearer token123' -print(headers['user-agent']) # 'MyApp/1.0' - -# Set with any case -headers['accept'] = 'application/json' -headers['CACHE-CONTROL'] = 'no-cache' - -# Check existence (case-insensitive) -print('Accept' in headers) # True -print('cache-control' in headers) # True - -# Delete with any case -del headers['USER-AGENT'] -print('user-agent' in headers) # False -``` - -### CaseInsensitiveDict Operations - -```python -from requests.structures import CaseInsensitiveDict - -# Create from various sources -headers1 = CaseInsensitiveDict([ - ('Content-Type', 'application/json'), - ('Accept', 'application/json') -]) - -headers2 = CaseInsensitiveDict( - authorization='Bearer token', - user_agent='MyApp/1.0' -) - -# Copy dictionary -headers_copy = headers1.copy() -headers_copy['X-Custom'] = 'custom-value' - -# Compare dictionaries (case-insensitive) -h1 = CaseInsensitiveDict({'Content-Type': 'application/json'}) -h2 = CaseInsensitiveDict({'content-type': 'application/json'}) -print(h1 == h2) # True (case-insensitive comparison) - -# Get lowercase items for processing -headers = CaseInsensitiveDict({ - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Authorization': 'Bearer token' -}) - -for lower_key, value in headers.lower_items(): - print(f"{lower_key}: {value}") -# content-type: application/json -# accept: application/json -# authorization: Bearer token -``` - -### LookupDict Usage - -```python -import requests - -# LookupDict is used for status codes -codes = requests.codes - -# Attribute access -print(codes.ok) # 200 -print(codes.not_found) # 404 -print(codes.internal_server_error) # 500 - -# Dictionary access -print(codes['ok']) # 200 -print(codes['not_found']) # 404 -print(codes['server_error']) # 500 - -# Both work the same way -status_code = 404 -if status_code == codes.not_found: - print("Resource not found") - -if status_code == codes['not_found']: - print("Resource not found") -``` - -### Custom LookupDict - -```python -from requests.structures import LookupDict - -# Create custom lookup dict -http_methods = LookupDict('HTTP Methods') -http_methods.update({ - 'get': 'GET', - 'post': 'POST', - 'put': 'PUT', - 'delete': 'DELETE', - 'head': 'HEAD', - 'options': 'OPTIONS', - 'patch': 'PATCH' -}) - -# Use both access methods -print(http_methods.get) # 'GET' -print(http_methods['post']) # 'POST' -print(http_methods.delete) # 'DELETE' - -# Get with default -print(http_methods.get('trace', 'TRACE')) # 'TRACE' -print(http_methods.get('connect')) # None -``` - -### Practical Header Management - -```python -import requests -from requests.structures import CaseInsensitiveDict - -def build_headers(content_type=None, auth_token=None, custom_headers=None): - """Build headers with case-insensitive handling.""" - headers = CaseInsensitiveDict() - - # Set standard headers - headers['User-Agent'] = 'MyApp/2.0' - - if content_type: - headers['Content-Type'] = content_type - - if auth_token: - headers['Authorization'] = f'Bearer {auth_token}' - - # Merge custom headers (case-insensitive) - if custom_headers: - for key, value in custom_headers.items(): - headers[key] = value - - return headers - -# Build headers -headers = build_headers( - content_type='application/json', - auth_token='abc123', - custom_headers={ - 'x-api-version': '2.0', - 'X-CUSTOM-HEADER': 'custom-value' - } -) - -# Use with request -response = requests.post('https://httpbin.org/post', - headers=headers, - json={'key': 'value'}) - -# Examine sent headers (case preserved) -sent_headers = response.request.headers -for name, value in sent_headers.items(): - print(f"{name}: {value}") -``` - -### Header Manipulation - -```python -import requests -from requests.structures import CaseInsensitiveDict - -# Start with response headers -response = requests.get('https://httpbin.org/response-headers?foo=bar') -headers = response.headers - -print(f"Original headers: {len(headers)} items") - -# Modify headers (case-insensitive) -modified_headers = headers.copy() -modified_headers['cache-control'] = 'no-cache' # Might overwrite Cache-Control -modified_headers['X-Custom'] = 'custom-value' # Add new header -del modified_headers['date'] # Remove Date header - -# Use modified headers in new request -new_response = requests.get('https://httpbin.org/headers', - headers=modified_headers) - -# Check what was sent -request_headers = new_response.json()['headers'] -for name, value in request_headers.items(): - print(f"{name}: {value}") -``` - -### Session Header Management - -```python -import requests -from requests.structures import CaseInsensitiveDict - -# Session headers are CaseInsensitiveDict -session = requests.Session() -print(type(session.headers)) # - -# Set session headers (case-insensitive) -session.headers.update({ - 'User-Agent': 'MyApp/1.0', - 'accept': 'application/json', - 'AUTHORIZATION': 'Bearer token123' -}) - -# Check headers with different cases -print('user-agent' in session.headers) # True -print(session.headers['Accept']) # 'application/json' -print(session.headers['authorization']) # 'Bearer token123' - -# Headers persist across requests -response1 = session.get('https://httpbin.org/headers') -response2 = session.post('https://httpbin.org/post', json={'data': 'test'}) - -# Both requests include the session headers -print("Request 1 headers:", response1.json()['headers']) -print("Request 2 headers:", response2.json()['headers']) -``` - -### Status Code Management - -```python -import requests -from requests.structures import LookupDict - -# The codes object is a LookupDict -print(type(requests.codes)) # - -# Create custom status mappings -api_codes = LookupDict('API Status Codes') -api_codes.update({ - 'success': 200, - 'created': 201, - 'accepted': 202, - 'bad_request': 400, - 'unauthorized': 401, - 'forbidden': 403, - 'not_found': 404, - 'server_error': 500 -}) - -# Use in response handling -def handle_api_response(response): - status = response.status_code - - if status == api_codes.success: - return response.json() - elif status == api_codes.created: - print("Resource created") - return response.json() - elif status == api_codes.not_found: - print("Resource not found") - return None - elif status == api_codes.server_error: - print("Server error occurred") - return None - else: - print(f"Unexpected status: {status}") - return None - -# Test with different responses -responses = [ - requests.get('https://httpbin.org/status/200'), - requests.get('https://httpbin.org/status/404'), - requests.get('https://httpbin.org/status/500') -] - -for resp in responses: - result = handle_api_response(resp) - print(f"Status {resp.status_code}: {result}") -``` - -## Structure Comparison - -### Standard Dict vs CaseInsensitiveDict - -```python -from requests.structures import CaseInsensitiveDict - -# Standard dict - case-sensitive -standard = {'Content-Type': 'application/json'} -print('content-type' in standard) # False -print('Content-Type' in standard) # True - -# CaseInsensitiveDict - case-insensitive -case_insensitive = CaseInsensitiveDict({'Content-Type': 'application/json'}) -print('content-type' in case_insensitive) # True -print('Content-Type' in case_insensitive) # True -print('CONTENT-TYPE' in case_insensitive) # True - -# Key preservation -print(list(standard.keys())) # ['Content-Type'] -print(list(case_insensitive.keys())) # ['Content-Type'] (original case preserved) - -# Lowercase iteration -for key, value in case_insensitive.lower_items(): - print(f"Lowercase: {key} -> {value}") # content-type -> application/json -``` - -### Standard Dict vs LookupDict - -```python -from requests.structures import LookupDict - -# Standard dict - item access only -standard = {'ok': 200, 'not_found': 404} -print(standard['ok']) # 200 -# print(standard.ok) # AttributeError - -# LookupDict - both item and attribute access -lookup = LookupDict() -lookup.update({'ok': 200, 'not_found': 404}) -print(lookup['ok']) # 200 -print(lookup.ok) # 200 (attribute access) -print(lookup.not_found) # 404 - -# Graceful handling of missing keys -print(lookup.get('missing')) # None -print(lookup.get('missing', 'default')) # 'default' -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-requests/tile.json b/.tessl/tiles/tessl/pypi-requests/tile.json deleted file mode 100644 index 8bdce0a67..000000000 --- a/.tessl/tiles/tessl/pypi-requests/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-requests", - "version": "2.32.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/requests@2.32.4", - "summary": "Python HTTP library for making HTTP requests with elegant API for humans." -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/console.md b/.tessl/tiles/tessl/pypi-rich/docs/console.md deleted file mode 100644 index a15f3e4c7..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/console.md +++ /dev/null @@ -1,494 +0,0 @@ -# Console and Output - -Core console functionality for printing styled text, handling terminal capabilities, and managing output formatting. The Console class is the central component of Rich, providing comprehensive terminal output management with automatic capability detection. - -## Capabilities - -### Console Class - -The main Console class handles all terminal output with support for styling, color, markup, and advanced formatting features. - -```python { .api } -class Console: - """ - A high level console interface for Rich. - - Args: - color_system: The color system to use ("auto", "standard", "256", "truecolor", "windows", None) - force_terminal: Force terminal detection - force_jupyter: Force Jupyter mode detection - force_interactive: Force interactive mode - soft_wrap: Enable soft wrapping of long lines - theme: Default theme or None for builtin theme - stderr: Write to stderr instead of stdout - file: File object to write to or None for stdout - quiet: Suppress all output if True - width: Width of output or None to auto-detect - height: Height of output or None to auto-detect - style: Default style for console output - no_color: Disable color output - tab_size: Size of tabs in characters - record: Record all output for later playback - markup: Enable Rich markup processing - emoji: Enable emoji code rendering - emoji_variant: Emoji variant preference - highlight: Enable highlighting of output - log_time: Include timestamp in log output - log_path: Include path in log output - log_time_format: Format for log timestamps - highlighter: Default highlighter for output - legacy_windows: Enable legacy Windows terminal support - safe_box: Disable box characters incompatible with legacy terminals - get_datetime: Callable to get current datetime - get_time: Callable to get current time - """ - def __init__( - self, - color_system: Optional[str] = "auto", - force_terminal: Optional[bool] = None, - force_jupyter: Optional[bool] = None, - force_interactive: Optional[bool] = None, - soft_wrap: bool = False, - theme: Optional[Theme] = None, - stderr: bool = False, - file: Optional[TextIO] = None, - quiet: bool = False, - width: Optional[int] = None, - height: Optional[int] = None, - style: Optional[StyleType] = None, - no_color: Optional[bool] = None, - tab_size: int = 8, - record: bool = False, - markup: bool = True, - emoji: bool = True, - emoji_variant: Optional[EmojiVariant] = None, - highlight: bool = True, - log_time: bool = True, - log_path: bool = True, - log_time_format: Union[str, FormatTimeCallable] = "[%X]", - highlighter: Optional[HighlighterType] = ReprHighlighter(), - legacy_windows: Optional[bool] = None, - safe_box: bool = True, - get_datetime: Optional[Callable[[], datetime]] = None, - get_time: Optional[Callable[[], float]] = None, - _environ: Optional[Mapping[str, str]] = None, - ): ... - - def print( - self, - *objects: Any, - sep: str = " ", - end: str = "\n", - style: Optional[StyleType] = None, - justify: Optional[JustifyMethod] = None, - overflow: Optional[OverflowMethod] = None, - no_wrap: Optional[bool] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - width: Optional[int] = None, - height: Optional[int] = None, - crop: bool = True, - soft_wrap: Optional[bool] = None, - new_line_start: bool = False, - ) -> None: - """ - Print to the console with rich formatting. - - Args: - *objects: Objects to print - sep: Separator between objects - end: String to print at end - style: Style to apply to output - justify: Text justification method - overflow: Overflow handling method - no_wrap: Disable text wrapping - emoji: Enable emoji rendering - markup: Enable markup processing - highlight: Enable highlighting - width: Maximum width of output - height: Maximum height of output - crop: Crop output to fit console - soft_wrap: Enable soft wrapping - new_line_start: Start on new line - """ - - def log( - self, - *objects: Any, - sep: str = " ", - end: str = "\n", - style: Optional[StyleType] = None, - justify: Optional[JustifyMethod] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - log_locals: bool = False, - _stack_offset: int = 1, - ) -> None: - """ - Print with timestamp and caller information. - - Args: - *objects: Objects to log - sep: Separator between objects - end: String to print at end - style: Style to apply to output - justify: Text justification method - emoji: Enable emoji rendering - markup: Enable markup processing - highlight: Enable highlighting - log_locals: Include local variables in output - """ - - def clear(self, home: bool = True) -> None: - """ - Clear the console. - - Args: - home: Move cursor to home position - """ - - def rule( - self, - title: TextType = "", - *, - characters: str = "─", - style: StyleType = "rule.line", - end: str = "\n", - align: AlignMethod = "center", - ) -> None: - """ - Print a horizontal rule with optional title. - - Args: - title: Title text for the rule - characters: Characters to use for the rule - style: Style for the rule - end: String to print at end - align: Alignment of title - """ - - def status( - self, - status: RenderableType, - *, - spinner: str = "dots", - spinner_style: StyleType = "status.spinner", - speed: float = 1.0, - ) -> "Status": - """ - Create a status context manager with spinner. - - Args: - status: Status text or renderable - spinner: Spinner style name - spinner_style: Style for spinner - speed: Spinner animation speed - - Returns: - Status context manager - """ - - def capture(self) -> "Capture": - """ - Create a capture context to record console output. - - Returns: - Capture context manager - """ - - def pager( - self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False - ) -> "PagerContext": - """ - Create a pager context for scrollable output. - - Args: - pager: Pager instance or None for system pager - styles: Include styles in paged output - links: Include links in paged output - - Returns: - Pager context manager - """ - - def screen( - self, *, hide_cursor: bool = True, alt_screen: bool = True - ) -> "ScreenContext": - """ - Create a screen context for full-screen applications. - - Args: - hide_cursor: Hide the cursor - alt_screen: Use alternate screen buffer - - Returns: - Screen context manager - """ - - def measure(self, renderable: RenderableType) -> Measurement: - """ - Measure the dimensions of a renderable. - - Args: - renderable: Object to measure - - Returns: - Measurement with minimum and maximum dimensions - """ - - def render( - self, renderable: RenderableType, options: Optional[ConsoleOptions] = None - ) -> Iterable[Segment]: - """ - Render a renderable to segments. - - Args: - renderable: Object to render - options: Console options or None for default - - Returns: - Iterator of segments - """ - - def export_html( - self, - *, - theme: Optional[TerminalTheme] = None, - clear: bool = True, - code_format: Optional[str] = None, - inline_styles: bool = False, - ) -> str: - """ - Export console content as HTML. - - Args: - theme: Terminal theme or None for default - clear: Clear console after export - code_format: Code format template - inline_styles: Use inline CSS styles - - Returns: - HTML string - """ - - def export_svg( - self, - *, - title: str = "Rich", - theme: Optional[TerminalTheme] = None, - clear: bool = True, - code_format: str = CONSOLE_SVG_FORMAT, - font_size: float = 14, - unique_id: Optional[str] = None, - ) -> str: - """ - Export console content as SVG. - - Args: - title: SVG title - theme: Terminal theme or None for default - clear: Clear console after export - code_format: SVG format template - font_size: Font size in pixels - unique_id: Unique identifier for SVG elements - - Returns: - SVG string - """ - - def export_text(self, *, clear: bool = True, styles: bool = False) -> str: - """ - Export console content as plain text. - - Args: - clear: Clear console after export - styles: Include ANSI styles in output - - Returns: - Plain text string - """ - - # Properties - @property - def size(self) -> ConsoleDimensions: - """Get console dimensions (width, height).""" - - @property - def width(self) -> int: - """Get console width in characters.""" - - @property - def height(self) -> int: - """Get console height in lines.""" - - @property - def is_terminal(self) -> bool: - """Check if output is a terminal.""" - - @property - def is_dumb_terminal(self) -> bool: - """Check if terminal has limited capabilities.""" - - @property - def is_interactive(self) -> bool: - """Check if console is interactive.""" - - @property - def color_system(self) -> Optional[str]: - """Get the active color system.""" - - @property - def encoding(self) -> str: - """Get the output encoding.""" - - @property - def is_jupyter(self) -> bool: - """Check if running in Jupyter.""" - - @property - def options(self) -> ConsoleOptions: - """Get default console options.""" -``` - -### Global Functions - -Module-level convenience functions for common operations. - -```python { .api } -def get_console() -> Console: - """ - Get a global Console instance. - - Returns: - Global console instance - """ - -def reconfigure(*args: Any, **kwargs: Any) -> None: - """ - Reconfigure the global console. - - Args: - *args: Positional arguments for Console constructor - **kwargs: Keyword arguments for Console constructor - """ - -def print( - *objects: Any, - sep: str = " ", - end: str = "\n", - file: Optional[IO[str]] = None, - flush: bool = False, -) -> None: - """ - Rich-enhanced print function with markup support. - - Args: - *objects: Objects to print - sep: Separator between objects - end: String to print at end - file: File to write to or None for stdout - flush: Force flush output (ignored, Rich always flushes) - """ - -def print_json( - json: Optional[str] = None, - *, - data: Any = None, - indent: Union[None, int, str] = 2, - highlight: bool = True, - skip_keys: bool = False, - ensure_ascii: bool = False, - check_circular: bool = True, - allow_nan: bool = True, - default: Optional[Callable[[Any], Any]] = None, - sort_keys: bool = False, -) -> None: - """ - Pretty print JSON with syntax highlighting. - - Args: - json: JSON string to print - data: Python data to encode as JSON - indent: Number of spaces to indent - highlight: Enable syntax highlighting - skip_keys: Skip keys that are not basic types - ensure_ascii: Escape non-ASCII characters - check_circular: Check for circular references - allow_nan: Allow NaN and Infinity values - default: Function to handle non-serializable objects - sort_keys: Sort object keys - """ - -def inspect( - obj: Any, - *, - console: Optional[Console] = None, - title: Optional[str] = None, - help: bool = False, - methods: bool = False, - docs: bool = True, - private: bool = False, - dunder: bool = False, - sort: bool = True, - all: bool = False, - value: bool = True, -) -> None: - """ - Inspect any Python object with rich formatting. - - Args: - obj: Object to inspect - console: Console instance or None for global console - title: Title for the inspection or None to use object type - help: Show full help text instead of summary - methods: Include callable methods - docs: Show docstrings - private: Show private attributes (starting with _) - dunder: Show dunder attributes (starting with __) - sort: Sort attributes alphabetically - all: Show all attributes (equivalent to private=True, dunder=True) - value: Show attribute values - """ -``` - -**Usage Examples:** - -```python -from rich.console import Console -from rich import print - -# Basic console usage -console = Console() -console.print("Hello, World!", style="bold red") - -# Enhanced print with markup -print("[bold blue]Information:[/bold blue] Process completed successfully") -print("[red]Error:[/red] Unable to connect to server") - -# Logging with timestamps -console.log("Application started") -console.log("Processing data...", style="yellow") - -# Status spinner -with console.status("Loading data...") as status: - time.sleep(2) - status.update("Processing...") - time.sleep(2) - -# Measuring content -from rich.text import Text -text = Text("Hello World", style="bold") -measurement = console.measure(text) -print(f"Text width: {measurement.minimum}-{measurement.maximum}") - -# Exporting output -console.print("This will be exported", style="green") -html_output = console.export_html() -svg_output = console.export_svg() - -# Object inspection -inspect([1, 2, 3, 4, 5]) -inspect(console, methods=True) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/containers.md b/.tessl/tiles/tessl/pypi-rich/docs/containers.md deleted file mode 100644 index 57182e97f..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/containers.md +++ /dev/null @@ -1,120 +0,0 @@ -# Panels and Containers - -Panels, rules, columns, and other container components for organizing content. Rich provides various container components for structuring and organizing terminal output. - -## Capabilities - -### Panel Class - -Decorative panels with borders and titles. - -```python { .api } -class Panel: - """ - Panel with border and optional title. - - Args: - renderable: Content to display in panel - box: Box style for borders - safe_box: Use safe box characters - expand: Expand to fit available width - style: Panel style - border_style: Border style - width: Fixed width or None for auto - height: Fixed height or None for auto - padding: Internal padding - highlight: Enable content highlighting - title: Panel title - title_align: Title alignment - subtitle: Panel subtitle - subtitle_align: Subtitle alignment - """ - def __init__( - self, - renderable: RenderableType, - box: Box = box.ROUNDED, - *, - safe_box: Optional[bool] = None, - expand: bool = True, - style: StyleType = "none", - border_style: StyleType = "none", - width: Optional[int] = None, - height: Optional[int] = None, - padding: PaddingDimensions = (0, 1), - highlight: bool = False, - title: Optional[TextType] = None, - title_align: AlignMethod = "center", - subtitle: Optional[TextType] = None, - subtitle_align: AlignMethod = "center", - ): ... - - @classmethod - def fit( - cls, - renderable: RenderableType, - box: Box = box.ROUNDED, - *, - safe_box: Optional[bool] = None, - style: StyleType = "none", - border_style: StyleType = "none", - padding: PaddingDimensions = (0, 1), - title: Optional[TextType] = None, - title_align: AlignMethod = "center", - ) -> "Panel": - """Create a panel that fits content exactly.""" - -class Rule: - """Horizontal rule with optional title.""" - def __init__( - self, - title: TextType = "", - *, - characters: str = "─", - style: StyleType = "rule.line", - end: str = "\n", - align: AlignMethod = "center", - ): ... - -class Columns: - """Multi-column layout for renderables.""" - def __init__( - self, - renderables: Optional[Iterable[RenderableType]] = None, - width: Optional[int] = None, - padding: PaddingDimensions = (0, 1), - expand: bool = False, - equal: bool = False, - column_first: bool = False, - right_to_left: bool = False, - align: AlignMethod = "left", - title: Optional[TextType] = None, - ): ... -``` - -**Usage Examples:** - -```python -from rich.console import Console -from rich.panel import Panel -from rich.rule import Rule -from rich.columns import Columns - -console = Console() - -# Basic panel -panel = Panel("Hello, World!", title="Greeting") -console.print(panel) - -# Rule -console.print(Rule("Section 1")) -console.print("Content here") -console.print(Rule()) - -# Columns -columns = Columns([ - Panel("Column 1"), - Panel("Column 2"), - Panel("Column 3") -]) -console.print(columns) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/index.md b/.tessl/tiles/tessl/pypi-rich/docs/index.md deleted file mode 100644 index 57d36526f..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/index.md +++ /dev/null @@ -1,592 +0,0 @@ -# Rich - -Rich is a comprehensive Python terminal formatting library that enables developers to create beautiful, interactive command-line interfaces with advanced text styling, color support, and rich output components. It provides a powerful API for rendering styled text with support for RGB/HEX colors, markup-style formatting, and automatic terminal capability detection. - -## Package Information - -- **Package Name**: rich -- **Package Type**: pypi -- **Language**: Python -- **Installation**: `pip install rich` -- **Minimum Python**: 3.8+ -- **Dependencies**: pygments, markdown-it-py - -## Core Imports - -```python -import rich -from rich.console import Console -from rich.text import Text -from rich import print -``` - -Common imports for specific functionality: - -```python -from rich.table import Table -from rich.progress import Progress -from rich.panel import Panel -from rich.syntax import Syntax -from rich.markdown import Markdown -from rich.prompt import Prompt -from rich.live import Live -from rich.layout import Layout -``` - -## Basic Usage - -```python -from rich.console import Console -from rich.text import Text -from rich import print - -# Enhanced print function with markup support -print("Hello [bold magenta]World[/bold magenta]!") -print("[bold red]Alert![/bold red] This is [italic]important[/italic].") - -# Console for advanced features -console = Console() -console.print("Hello", "World!", style="bold blue") - -# Rich text objects -text = Text("Hello World!") -text.stylize("bold magenta", 0, 6) -console.print(text) - -# Simple table -from rich.table import Table -table = Table() -table.add_column("Name") -table.add_column("Score") -table.add_row("Alice", "95") -table.add_row("Bob", "87") -console.print(table) -``` - -## Architecture - -Rich is built around several key components: - -- **Console**: Central output manager handling terminal detection, rendering, and export -- **Text Objects**: Styled text with spans for precise formatting control -- **Renderables**: Protocol for objects that can be rendered to the terminal -- **Styles**: Comprehensive styling system with color, typography, and effects -- **Layout System**: Advanced positioning and sizing for complex UIs -- **Live Display**: Real-time updating content for progress bars and dynamic interfaces - -## Capabilities - -### Console Output and Styling - -Core console functionality for printing styled text, handling terminal capabilities, and managing output formatting. - -```python { .api } -class Console: - def __init__( - self, - color_system: Optional[str] = "auto", - force_terminal: Optional[bool] = None, - force_jupyter: Optional[bool] = None, - force_interactive: Optional[bool] = None, - soft_wrap: bool = False, - theme: Optional[Theme] = None, - stderr: bool = False, - file: Optional[TextIO] = None, - quiet: bool = False, - width: Optional[int] = None, - height: Optional[int] = None, - style: Optional[StyleType] = None, - no_color: Optional[bool] = None, - tab_size: int = 8, - record: bool = False, - markup: bool = True, - emoji: bool = True, - emoji_variant: Optional[EmojiVariant] = None, - highlight: bool = True, - log_time: bool = True, - log_path: bool = True, - log_time_format: Union[str, FormatTimeCallable] = "[%X]", - highlighter: Optional[HighlighterType] = ReprHighlighter(), - legacy_windows: Optional[bool] = None, - safe_box: bool = True, - get_datetime: Optional[Callable[[], datetime]] = None, - get_time: Optional[Callable[[], float]] = None, - _environ: Optional[Mapping[str, str]] = None, - ): ... - - def print( - self, - *objects: Any, - sep: str = " ", - end: str = "\n", - style: Optional[StyleType] = None, - justify: Optional[JustifyMethod] = None, - overflow: Optional[OverflowMethod] = None, - no_wrap: Optional[bool] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - width: Optional[int] = None, - height: Optional[int] = None, - crop: bool = True, - soft_wrap: Optional[bool] = None, - new_line_start: bool = False, - ) -> None: ... - -def print( - *objects: Any, - sep: str = " ", - end: str = "\n", - file: Optional[IO[str]] = None, - flush: bool = False, -) -> None: ... - -def print_json( - json: Optional[str] = None, - *, - data: Any = None, - indent: Union[None, int, str] = 2, - highlight: bool = True, - skip_keys: bool = False, - ensure_ascii: bool = False, - check_circular: bool = True, - allow_nan: bool = True, - default: Optional[Callable[[Any], Any]] = None, - sort_keys: bool = False, -) -> None: ... -``` - -[Console and Output](./console.md) - -### Rich Text and Styling - -Advanced text objects with precise styling control, markup support, and comprehensive formatting options. - -```python { .api } -class Text: - def __init__( - self, - text: str = "", - style: StyleType = "", - *, - justify: Optional[JustifyMethod] = None, - overflow: OverflowMethod = "fold", - no_wrap: Optional[bool] = None, - end: str = "\n", - tab_size: Optional[int] = 8, - spans: Optional[List[Span]] = None, - ): ... - - def stylize( - self, - style: StyleType, - start: int = 0, - end: Optional[int] = None, - ) -> None: ... - - @classmethod - def from_markup( - cls, - text: str, - *, - style: StyleType = "", - emoji: bool = True, - emoji_variant: Optional[EmojiVariant] = None, - ) -> "Text": ... - -class Style: - def __init__( - self, - *, - color: Optional[ColorType] = None, - bgcolor: Optional[ColorType] = None, - bold: Optional[bool] = None, - dim: Optional[bool] = None, - italic: Optional[bool] = None, - underline: Optional[bool] = None, - blink: Optional[bool] = None, - blink2: Optional[bool] = None, - reverse: Optional[bool] = None, - conceal: Optional[bool] = None, - strike: Optional[bool] = None, - underline2: Optional[bool] = None, - frame: Optional[bool] = None, - encircle: Optional[bool] = None, - overline: Optional[bool] = None, - link: Optional[str] = None, - meta: Optional[Dict[str, Any]] = None, - ): ... -``` - -[Text and Styling](./text-styling.md) - -### Tables and Data Display - -Comprehensive table system with flexible columns, styling, borders, and automatic sizing. - -```python { .api } -class Table: - def __init__( - self, - *columns: Union[Column, str], - title: Optional[TextType] = None, - caption: Optional[TextType] = None, - width: Optional[int] = None, - min_width: Optional[int] = None, - box: Optional[Box] = box.HEAVY_HEAD, - safe_box: Optional[bool] = None, - padding: PaddingDimensions = (0, 1), - collapse_padding: bool = False, - pad_edge: bool = True, - expand: bool = False, - show_header: bool = True, - show_footer: bool = False, - show_edge: bool = True, - show_lines: bool = False, - leading: int = 0, - style: StyleType = "", - row_styles: Optional[Iterable[StyleType]] = None, - header_style: Optional[StyleType] = None, - footer_style: Optional[StyleType] = None, - border_style: Optional[StyleType] = None, - title_style: Optional[StyleType] = None, - caption_style: Optional[StyleType] = None, - title_justify: JustifyMethod = "center", - caption_justify: JustifyMethod = "center", - highlight: bool = False, - ): ... - - def add_column( - self, - header: RenderableType = "", - *, - footer: RenderableType = "", - header_style: Optional[StyleType] = None, - footer_style: Optional[StyleType] = None, - style: Optional[StyleType] = None, - justify: JustifyMethod = "left", - vertical: VerticalAlignMethod = "top", - overflow: OverflowMethod = "ellipsis", - width: Optional[int] = None, - min_width: Optional[int] = None, - max_width: Optional[int] = None, - ratio: Optional[int] = None, - no_wrap: bool = False, - ) -> None: ... - - def add_row( - self, - *renderables: Optional[RenderableType], - style: Optional[StyleType] = None, - end_section: bool = False, - ) -> None: ... -``` - -[Tables](./tables.md) - -### Progress Tracking - -Advanced progress bar system with customizable columns, multiple tasks, and real-time updates. - -```python { .api } -class Progress: - def __init__( - self, - *columns: Union[str, ProgressColumn], - console: Optional[Console] = None, - auto_refresh: bool = True, - refresh_per_second: float = 10, - speed_estimate_period: float = 30.0, - transient: bool = False, - redirect_stdout: bool = True, - redirect_stderr: bool = True, - get_time: Optional[GetTimeCallable] = None, - disable: bool = False, - expand: bool = False, - ): ... - - def add_task( - self, - description: str, - start: bool = True, - total: Optional[float] = 100.0, - completed: float = 0, - visible: bool = True, - **fields: Any, - ) -> TaskID: ... - - def update( - self, - task_id: TaskID, - *, - total: Optional[float] = None, - completed: Optional[float] = None, - advance: Optional[float] = None, - description: Optional[str] = None, - visible: Optional[bool] = None, - refresh: bool = False, - **fields: Any, - ) -> None: ... - - def track( - self, - sequence: Iterable[ProgressType], - task_id: Optional[TaskID] = None, - description: str = "Working...", - total: Optional[float] = None, - auto_refresh: bool = True, - console: Optional[Console] = None, - transient: bool = False, - get_time: Optional[GetTimeCallable] = None, - refresh_per_second: float = 10, - style: StyleType = "bar.back", - complete_style: StyleType = "bar.complete", - finished_style: StyleType = "bar.finished", - pulse_style: StyleType = "bar.pulse", - update_period: float = 0.1, - disable: bool = False, - show_speed: bool = True, - ) -> Iterable[ProgressType]: ... -``` - -[Progress Tracking](./progress.md) - -### Syntax Highlighting - -Code syntax highlighting with theme support and extensive language coverage via Pygments. - -```python { .api } -class Syntax: - def __init__( - self, - code: str, - lexer: Optional[Union[Lexer, str]] = None, - *, - theme: Union[str, SyntaxTheme] = "monokai", - dedent: bool = False, - line_numbers: bool = False, - start_line: int = 1, - line_range: Optional[Tuple[int, int]] = None, - highlight_lines: Optional[Set[int]] = None, - code_width: Optional[int] = None, - tab_size: int = 4, - word_wrap: bool = False, - background_color: Optional[str] = None, - indent_guides: bool = False, - padding: PaddingDimensions = 0, - ): ... - - @classmethod - def from_path( - cls, - path: Union[str, PathLike[str]], - encoding: str = "utf-8", - lexer: Optional[Union[Lexer, str]] = None, - theme: Union[str, SyntaxTheme] = "monokai", - dedent: bool = False, - line_numbers: bool = False, - line_range: Optional[Tuple[int, int]] = None, - start_line: int = 1, - highlight_lines: Optional[Set[int]] = None, - code_width: Optional[int] = None, - tab_size: int = 4, - word_wrap: bool = False, - background_color: Optional[str] = None, - indent_guides: bool = False, - padding: PaddingDimensions = 0, - ) -> "Syntax": ... -``` - -[Syntax Highlighting](./syntax.md) - -### Interactive Components - -Live updating displays, user input prompts, and real-time interface components. - -```python { .api } -class Live: - def __init__( - self, - renderable: Optional[RenderableType] = None, - *, - console: Optional[Console] = None, - screen: bool = False, - auto_refresh: bool = True, - refresh_per_second: float = 4, - transient: bool = False, - redirect_stdout: bool = True, - redirect_stderr: bool = True, - vertical_overflow: OverflowMethod = "ellipsis", - get_renderable: Optional[Callable[[], RenderableType]] = None, - ): ... - -class Prompt: - @classmethod - def ask( - cls, - prompt: TextType = "", - *, - console: Optional[Console] = None, - password: bool = False, - choices: Optional[List[str]] = None, - show_default: bool = True, - show_choices: bool = True, - default: Any = ..., - stream: Optional[TextIO] = None, - ) -> Any: ... -``` - -[Interactive Components](./interactive.md) - -### Layout and Positioning - -Advanced layout system for complex terminal UIs with flexible positioning and sizing. - -```python { .api } -class Layout: - def __init__( - self, - renderable: Optional[RenderableType] = None, - *, - name: str = "root", - size: Optional[int] = None, - minimum_size: int = 1, - ratio: int = 1, - visible: bool = True, - ): ... - - def split( - self, - *layouts: Union["Layout", RenderableType], - splitter: Splitter = "column", - ) -> None: ... - - def split_column(self, *layouts: Union["Layout", RenderableType]) -> None: ... - def split_row(self, *layouts: Union["Layout", RenderableType]) -> None: ... -``` - -[Layout System](./layout.md) - -### Markdown Rendering - -CommonMark-compliant markdown rendering with syntax highlighting and Rich formatting. - -```python { .api } -class Markdown: - def __init__( - self, - markup: str, - *, - code_theme: Union[str, SyntaxTheme] = "monokai", - justify: Optional[JustifyMethod] = None, - style: StyleType = "none", - hyperlinks: bool = True, - inline_code_lexer: Optional[str] = None, - inline_code_theme: Optional[Union[str, SyntaxTheme]] = None, - ): ... -``` - -[Markdown](./markdown.md) - -### Panel and Container Components - -Panels, rules, columns, and other container components for organizing content. - -```python { .api } -class Panel: - def __init__( - self, - renderable: RenderableType, - box: Box = box.ROUNDED, - *, - safe_box: Optional[bool] = None, - expand: bool = True, - style: StyleType = "none", - border_style: StyleType = "none", - width: Optional[int] = None, - height: Optional[int] = None, - padding: PaddingDimensions = (0, 1), - highlight: bool = False, - title: Optional[TextType] = None, - title_align: AlignMethod = "center", - subtitle: Optional[TextType] = None, - subtitle_align: AlignMethod = "center", - ): ... - -class Rule: - def __init__( - self, - title: TextType = "", - *, - characters: str = "─", - style: StyleType = "rule.line", - end: str = "\n", - align: AlignMethod = "center", - ): ... -``` - -[Panels and Containers](./containers.md) - -### Utilities and Helpers - -Color handling, measurement, error types, and other utility functions. - -```python { .api } -class Color: - @classmethod - def parse(cls, color: ColorType) -> "Color": ... - - def blend(self, destination: "Color", factor: float) -> "Color": ... - -def inspect( - obj: Any, - *, - console: Optional[Console] = None, - title: Optional[str] = None, - help: bool = False, - methods: bool = False, - docs: bool = True, - private: bool = False, - dunder: bool = False, - sort: bool = True, - all: bool = False, - value: bool = True, -) -> None: ... -``` - -[Utilities](./utilities.md) - -## Types - -```python { .api } -from typing import Union, Optional, IO, Any, Callable, List, Dict, Tuple, Iterable - -# Core types -TextType = Union[str, Text] -StyleType = Union[str, Style] -RenderableType = Union[ConsoleRenderable, RichRenderable, str] -ColorType = Union[int, str, Tuple[int, int, int], Color] - -# Method types -JustifyMethod = Literal["default", "left", "center", "right", "full"] -OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"] -AlignMethod = Literal["left", "center", "right"] -VerticalAlignMethod = Literal["top", "middle", "bottom"] - -# Progress types -TaskID = NewType("TaskID", int) -GetTimeCallable = Callable[[], float] - -# Console types -HighlighterType = Callable[[Union[str, Text]], Text] -FormatTimeCallable = Callable[[datetime], Text] - -# Padding type -PaddingDimensions = Union[ - int, - Tuple[int], - Tuple[int, int], - Tuple[int, int, int], - Tuple[int, int, int, int], -] -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/interactive.md b/.tessl/tiles/tessl/pypi-rich/docs/interactive.md deleted file mode 100644 index cb7d0346c..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/interactive.md +++ /dev/null @@ -1,587 +0,0 @@ -# Interactive Components - -Live updating displays, user input prompts, and real-time interface components. Rich provides comprehensive interactive capabilities for dynamic terminal applications and user input handling. - -## Capabilities - -### Live Display - -Real-time updating display system for dynamic content. - -```python { .api } -class Live: - """ - Live updating display context manager. - - Args: - renderable: Initial content to display - console: Console instance for output - screen: Use full screen mode - auto_refresh: Enable automatic refresh - refresh_per_second: Refresh rate in Hz - transient: Remove display when exiting context - redirect_stdout: Redirect stdout during live display - redirect_stderr: Redirect stderr during live display - vertical_overflow: Overflow method for vertical content - get_renderable: Function to get current renderable - """ - def __init__( - self, - renderable: Optional[RenderableType] = None, - *, - console: Optional[Console] = None, - screen: bool = False, - auto_refresh: bool = True, - refresh_per_second: float = 4, - transient: bool = False, - redirect_stdout: bool = True, - redirect_stderr: bool = True, - vertical_overflow: OverflowMethod = "ellipsis", - get_renderable: Optional[Callable[[], RenderableType]] = None, - ): ... - - def update( - self, - renderable: RenderableType, - *, - refresh: bool = False - ) -> None: - """ - Update the live display content. - - Args: - renderable: New content to display - refresh: Force immediate refresh - """ - - def refresh(self) -> None: - """Force refresh of the display.""" - - def start(self, refresh: bool = False) -> None: - """ - Start the live display. - - Args: - refresh: Refresh immediately after starting - """ - - def stop(self) -> None: - """Stop the live display.""" - - # Properties - @property - def is_started(self) -> bool: - """Check if live display is active.""" - - @property - def renderable(self) -> RenderableType: - """Get current renderable content.""" - - @property - def console(self) -> Console: - """Get the console instance.""" -``` - -### Status Display - -Status indicator with animated spinner. - -```python { .api } -class Status: - """ - Status display with spinner animation. - - Args: - status: Status message or renderable - console: Console instance for output - spinner: Spinner animation name - spinner_style: Style for spinner - speed: Animation speed multiplier - refresh_per_second: Refresh rate in Hz - """ - def __init__( - self, - status: RenderableType, - *, - console: Optional[Console] = None, - spinner: str = "dots", - spinner_style: StyleType = "status.spinner", - speed: float = 1.0, - refresh_per_second: float = 12.5, - ): ... - - def update( - self, - status: Optional[RenderableType] = None, - *, - spinner: Optional[str] = None, - spinner_style: Optional[StyleType] = None, - speed: Optional[float] = None, - ) -> None: - """ - Update status display. - - Args: - status: New status message - spinner: New spinner style - spinner_style: New spinner styling - speed: New animation speed - """ - - def start(self) -> None: - """Start the status display.""" - - def stop(self) -> None: - """Stop the status display.""" - - # Properties - @property - def renderable(self) -> RenderableType: - """Get current status renderable.""" -``` - -### User Input Prompts - -Interactive prompts for user input with validation. - -```python { .api } -class PromptBase(Generic[PromptType]): - """Base class for user input prompts.""" - - @classmethod - def ask( - cls, - prompt: TextType = "", - *, - console: Optional[Console] = None, - password: bool = False, - choices: Optional[List[str]] = None, - show_default: bool = True, - show_choices: bool = True, - default: Any = ..., - stream: Optional[TextIO] = None, - ) -> PromptType: - """ - Prompt user for input. - - Args: - prompt: Prompt text to display - console: Console instance for I/O - password: Hide input for passwords - choices: Valid choices for input - show_default: Show default value in prompt - show_choices: Show available choices - default: Default value if no input - stream: Input stream or None for stdin - - Returns: - User input converted to appropriate type - """ - - def get_input( - self, - console: Console, - prompt: TextType, - password: bool, - stream: Optional[TextIO] = None, - ) -> str: - """ - Get raw input from user. - - Args: - console: Console for output - prompt: Prompt to display - password: Hide input characters - stream: Input stream - - Returns: - Raw input string - """ - - def check_choice(self, value: str) -> bool: - """ - Check if input matches available choices. - - Args: - value: User input to check - - Returns: - True if valid choice - """ - - def process_response(self, value: str) -> PromptType: - """ - Process and validate user response. - - Args: - value: Raw user input - - Returns: - Processed and validated response - """ - - def on_validate_error(self, value: str, error: ValueError) -> None: - """ - Handle validation errors. - - Args: - value: Invalid input value - error: Validation error - """ - - def render_default(self, default: Any) -> Text: - """ - Render default value for display. - - Args: - default: Default value - - Returns: - Formatted default value - """ - -class Prompt(PromptBase[str]): - """String input prompt.""" - - @classmethod - def ask( - cls, - prompt: TextType = "", - *, - console: Optional[Console] = None, - password: bool = False, - choices: Optional[List[str]] = None, - show_default: bool = True, - show_choices: bool = True, - default: str = "", - stream: Optional[TextIO] = None, - ) -> str: - """ - Prompt for string input. - - Args: - prompt: Prompt text - console: Console instance - password: Hide input - choices: Valid string choices - show_default: Show default in prompt - show_choices: Show available choices - default: Default string value - stream: Input stream - - Returns: - User input string - """ - -class IntPrompt(PromptBase[int]): - """Integer input prompt.""" - - @classmethod - def ask( - cls, - prompt: TextType = "", - *, - console: Optional[Console] = None, - password: bool = False, - choices: Optional[List[int]] = None, - show_default: bool = True, - show_choices: bool = True, - default: int = ..., - stream: Optional[TextIO] = None, - ) -> int: - """ - Prompt for integer input. - - Args: - prompt: Prompt text - console: Console instance - password: Hide input - choices: Valid integer choices - show_default: Show default in prompt - show_choices: Show available choices - default: Default integer value - stream: Input stream - - Returns: - User input as integer - """ - -class FloatPrompt(PromptBase[float]): - """Float input prompt.""" - - @classmethod - def ask( - cls, - prompt: TextType = "", - *, - console: Optional[Console] = None, - password: bool = False, - choices: Optional[List[float]] = None, - show_default: bool = True, - show_choices: bool = True, - default: float = ..., - stream: Optional[TextIO] = None, - ) -> float: - """ - Prompt for float input. - - Args: - prompt: Prompt text - console: Console instance - password: Hide input - choices: Valid float choices - show_default: Show default in prompt - show_choices: Show available choices - default: Default float value - stream: Input stream - - Returns: - User input as float - """ - -class Confirm(PromptBase[bool]): - """Yes/no confirmation prompt.""" - - @classmethod - def ask( - cls, - prompt: TextType = "", - *, - console: Optional[Console] = None, - password: bool = False, - choices: Optional[List[str]] = None, - show_default: bool = True, - show_choices: bool = True, - default: bool = False, - stream: Optional[TextIO] = None, - ) -> bool: - """ - Prompt for yes/no confirmation. - - Args: - prompt: Prompt text - console: Console instance - password: Hide input (not recommended for confirm) - choices: Custom yes/no choices - show_default: Show default in prompt - show_choices: Show y/n choices - default: Default boolean value - stream: Input stream - - Returns: - True for yes, False for no - """ -``` - -### Spinner Animations - -Animated spinner components for loading indicators. - -```python { .api } -class Spinner: - """ - Animated spinner for loading indicators. - - Args: - name: Spinner animation name - text: Text to display with spinner - style: Style for spinner and text - speed: Animation speed multiplier - """ - def __init__( - self, - name: str = "dots", - text: TextType = "", - *, - style: Optional[StyleType] = None, - speed: float = 1.0, - ): ... - - def render(self, time: float) -> RenderableType: - """ - Render spinner at given time. - - Args: - time: Current time for animation - - Returns: - Renderable spinner frame - """ - - # Properties - @property - def name(self) -> str: - """Get spinner name.""" - - @property - def frames(self) -> List[str]: - """Get animation frames.""" - - @property - def interval(self) -> float: - """Get frame interval in seconds.""" -``` - -**Usage Examples:** - -```python -from rich.live import Live -from rich.console import Console -from rich.table import Table -from rich.spinner import Spinner -from rich.status import Status -from rich.prompt import Prompt, IntPrompt, FloatPrompt, Confirm -import time - -console = Console() - -# Basic live display -def generate_table(): - """Generate a sample table.""" - table = Table() - table.add_column("ID") - table.add_column("Name") - table.add_column("Status") - - table.add_row("1", "Task A", "Running") - table.add_row("2", "Task B", "Complete") - table.add_row("3", "Task C", "Pending") - - return table - -with Live(generate_table(), refresh_per_second=4) as live: - for i in range(40): - time.sleep(0.1) - # Update with new content - table = generate_table() - # Add dynamic row - table.add_row(str(i+4), f"Dynamic {i}", "Active") - live.update(table) - -# Live display with periodic updates -from rich.text import Text - -def get_current_time(): - return Text(f"Current time: {time.strftime('%H:%M:%S')}", style="bold green") - -with Live(get_current_time(), refresh_per_second=1) as live: - for _ in range(10): - time.sleep(1) - live.update(get_current_time()) - -# Status with spinner -with console.status("Loading data...") as status: - time.sleep(2) - status.update("Processing data...") - time.sleep(2) - status.update("Finalizing...") - time.sleep(1) - -# Custom status display -status = Status( - "Custom loading...", - spinner="dots", - spinner_style="bold blue" -) - -with status: - time.sleep(3) - -# User input prompts -name = Prompt.ask("What's your name?") -print(f"Hello {name}!") - -age = IntPrompt.ask("What's your age?", default=25) -print(f"You are {age} years old") - -height = FloatPrompt.ask("What's your height in meters?", default=1.75) -print(f"Your height is {height}m") - -# Confirmation prompt -delete_files = Confirm.ask("Delete all files?") -if delete_files: - print("Files would be deleted") -else: - print("Operation cancelled") - -# Prompt with choices -color = Prompt.ask( - "Choose a color", - choices=["red", "green", "blue"], - default="blue" -) -print(f"You chose {color}") - -# Password input -password = Prompt.ask("Enter password", password=True) -print("Password entered (hidden)") - -# Complex live display with multiple updates -from rich.panel import Panel -from rich.columns import Columns - -def create_dashboard(): - """Create a dashboard layout.""" - left_panel = Panel("System Status: OK", title="Status") - right_panel = Panel(f"Time: {time.strftime('%H:%M:%S')}", title="Clock") - - return Columns([left_panel, right_panel]) - -with Live(create_dashboard(), refresh_per_second=1) as live: - for i in range(20): - time.sleep(0.5) - if i % 4 == 0: # Update every 2 seconds - live.update(create_dashboard()) - -# Progress with live updates -from rich.progress import Progress, TaskID - -def live_progress_demo(): - """Demonstrate live progress updates.""" - progress = Progress() - - with Live(progress, refresh_per_second=10) as live: - task1 = progress.add_task("Download", total=1000) - task2 = progress.add_task("Process", total=1000) - - while not progress.finished: - progress.advance(task1, 3) - progress.advance(task2, 1) - time.sleep(0.02) - -# Live display with error handling -def safe_live_display(): - """Live display with error handling.""" - try: - with Live("Starting...", transient=True) as live: - for i in range(5): - live.update(f"Step {i+1}/5") - time.sleep(1) - live.update("Complete!") - time.sleep(1) - except KeyboardInterrupt: - console.print("Display interrupted by user") - -# Interactive menu using prompts -def interactive_menu(): - """Create an interactive menu system.""" - while True: - console.print("\n[bold blue]Main Menu[/bold blue]") - console.print("1. View Status") - console.print("2. Update Settings") - console.print("3. Exit") - - choice = IntPrompt.ask("Choose option", choices=[1, 2, 3]) - - if choice == 1: - console.print("[green]Status: All systems operational[/green]") - elif choice == 2: - console.print("[yellow]Settings updated[/yellow]") - elif choice == 3: - if Confirm.ask("Really exit?"): - break - - console.print("Goodbye!") - -# Example: interactive_menu() -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/layout.md b/.tessl/tiles/tessl/pypi-rich/docs/layout.md deleted file mode 100644 index 715873e4a..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/layout.md +++ /dev/null @@ -1,113 +0,0 @@ -# Layout System - -Advanced layout system for complex terminal UIs with flexible positioning and sizing. Rich provides powerful layout management for creating sophisticated terminal applications with responsive design. - -## Capabilities - -### Layout Class - -Main layout management system with flexible splitting and positioning. - -```python { .api } -class Layout: - """ - Layout management for terminal UIs. - - Args: - renderable: Initial content to display - name: Layout identifier - size: Fixed size or None for flexible - minimum_size: Minimum size constraint - ratio: Size ratio for flexible layouts - visible: Layout visibility - """ - def __init__( - self, - renderable: Optional[RenderableType] = None, - *, - name: str = "root", - size: Optional[int] = None, - minimum_size: int = 1, - ratio: int = 1, - visible: bool = True, - ): ... - - def split( - self, - *layouts: Union["Layout", RenderableType], - splitter: Splitter = "column", - ) -> None: - """ - Split layout into multiple sections. - - Args: - *layouts: Child layouts or renderables - splitter: Split direction ("column" or "row") - """ - - def split_column(self, *layouts: Union["Layout", RenderableType]) -> None: - """Split layout vertically.""" - - def split_row(self, *layouts: Union["Layout", RenderableType]) -> None: - """Split layout horizontally.""" - - def add_split(self, *layouts: Union["Layout", RenderableType]) -> None: - """Add layouts to existing split.""" - - def unsplit(self) -> "Layout": - """Remove split and return to single layout.""" - - def update(self, renderable: RenderableType) -> None: - """Update layout content.""" - - def refresh_screen(self, console: Console, size: ConsoleDimensions) -> None: - """Refresh layout display.""" - -class Splitter(ABC): - """Abstract base for layout splitters.""" - - @abstractmethod - def divide( - self, - children: Sequence[Layout], - size: int - ) -> List[int]: ... - -class RowSplitter(Splitter): - """Horizontal layout splitter.""" - -class ColumnSplitter(Splitter): - """Vertical layout splitter.""" -``` - -**Usage Examples:** - -```python -from rich.console import Console -from rich.layout import Layout -from rich.panel import Panel - -console = Console() - -# Basic layout -layout = Layout() -layout.split_column( - Layout(Panel("Header", style="blue")), - Layout(Panel("Content", style="green")), - Layout(Panel("Footer", style="red")) -) -console.print(layout) - -# Complex layout with ratios -layout = Layout() -layout.split_column( - Layout(Panel("Header"), size=3), - Layout(name="main"), - Layout(Panel("Footer"), size=3) -) -layout["main"].split_row( - Layout(Panel("Left"), name="left"), - Layout(Panel("Right"), name="right") -) -console.print(layout) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/markdown.md b/.tessl/tiles/tessl/pypi-rich/docs/markdown.md deleted file mode 100644 index dd632e5d6..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/markdown.md +++ /dev/null @@ -1,65 +0,0 @@ -# Markdown - -CommonMark-compliant markdown rendering with syntax highlighting and Rich formatting. Rich provides comprehensive markdown support for displaying formatted documents in the terminal. - -## Capabilities - -### Markdown Class - -Main markdown rendering component with full CommonMark support. - -```python { .api } -class Markdown: - """ - Markdown document renderer. - - Args: - markup: Markdown text to render - code_theme: Theme for code blocks - justify: Text justification - style: Base style for markdown - hyperlinks: Enable hyperlink rendering - inline_code_lexer: Lexer for inline code - inline_code_theme: Theme for inline code - """ - def __init__( - self, - markup: str, - *, - code_theme: Union[str, SyntaxTheme] = "monokai", - justify: Optional[JustifyMethod] = None, - style: StyleType = "none", - hyperlinks: bool = True, - inline_code_lexer: Optional[str] = None, - inline_code_theme: Optional[Union[str, SyntaxTheme]] = None, - ): ... -``` - -**Usage Examples:** - -```python -from rich.console import Console -from rich.markdown import Markdown - -console = Console() - -markdown_text = """ -# My Document - -This is **bold** and *italic* text. - -## Code Example - -```python -def hello(): - print("Hello, World!") -``` - -- List item 1 -- List item 2 -- List item 3 -""" - -md = Markdown(markdown_text) -console.print(md) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/progress.md b/.tessl/tiles/tessl/pypi-rich/docs/progress.md deleted file mode 100644 index e01e4d6ec..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/progress.md +++ /dev/null @@ -1,608 +0,0 @@ -# Progress Tracking - -Advanced progress bar system with customizable columns, multiple tasks, and real-time updates. Rich provides comprehensive progress tracking capabilities for long-running operations with flexible display options. - -## Capabilities - -### Progress Class - -Main progress tracking system with support for multiple concurrent tasks. - -```python { .api } -class Progress: - """ - Multi-task progress tracker with customizable display. - - Args: - *columns: Progress display columns - console: Console instance for output - auto_refresh: Enable automatic refresh - refresh_per_second: Refresh rate in Hz - speed_estimate_period: Period for speed calculation - transient: Remove progress display when complete - redirect_stdout: Redirect stdout during progress - redirect_stderr: Redirect stderr during progress - get_time: Function to get current time - disable: Disable progress display - expand: Expand progress bar to fit console width - """ - def __init__( - self, - *columns: Union[str, ProgressColumn], - console: Optional[Console] = None, - auto_refresh: bool = True, - refresh_per_second: float = 10, - speed_estimate_period: float = 30.0, - transient: bool = False, - redirect_stdout: bool = True, - redirect_stderr: bool = True, - get_time: Optional[GetTimeCallable] = None, - disable: bool = False, - expand: bool = False, - ): ... - - def add_task( - self, - description: str, - start: bool = True, - total: Optional[float] = 100.0, - completed: float = 0, - visible: bool = True, - **fields: Any, - ) -> TaskID: - """ - Add a new progress task. - - Args: - description: Task description - start: Start the task immediately - total: Total amount of work or None for indeterminate - completed: Initial completed amount - visible: Show task in display - **fields: Additional custom fields - - Returns: - Task ID for future operations - """ - - def remove_task(self, task_id: TaskID) -> None: - """ - Remove a task from progress tracking. - - Args: - task_id: ID of task to remove - """ - - def update( - self, - task_id: TaskID, - *, - total: Optional[float] = None, - completed: Optional[float] = None, - advance: Optional[float] = None, - description: Optional[str] = None, - visible: Optional[bool] = None, - refresh: bool = False, - **fields: Any, - ) -> None: - """ - Update a progress task. - - Args: - task_id: Task to update - total: New total amount - completed: New completed amount - advance: Amount to advance by - description: New description - visible: Show/hide task - refresh: Force display refresh - **fields: Update custom fields - """ - - def advance(self, task_id: TaskID, advance: float = 1.0) -> None: - """ - Advance a task by specified amount. - - Args: - task_id: Task to advance - advance: Amount to advance by - """ - - def start_task(self, task_id: TaskID) -> None: - """ - Start a paused task. - - Args: - task_id: Task to start - """ - - def stop_task(self, task_id: TaskID) -> None: - """ - Stop/pause a task. - - Args: - task_id: Task to stop - """ - - def reset( - self, - task_id: TaskID, - *, - start: bool = True, - total: Optional[float] = None, - completed: float = 0, - visible: Optional[bool] = None, - description: Optional[str] = None, - **fields: Any, - ) -> None: - """ - Reset a task to initial state. - - Args: - task_id: Task to reset - start: Start task after reset - total: New total amount - completed: Reset completed amount - visible: Task visibility - description: New description - **fields: Reset custom fields - """ - - def get_task(self, task_id: TaskID) -> Task: - """ - Get task information. - - Args: - task_id: Task ID - - Returns: - Task object with current state - """ - - def refresh(self) -> None: - """Force refresh of progress display.""" - - def start(self) -> None: - """Start progress display.""" - - def stop(self) -> None: - """Stop progress display.""" - - def track( - self, - sequence: Iterable[ProgressType], - task_id: Optional[TaskID] = None, - description: str = "Working...", - total: Optional[float] = None, - auto_refresh: bool = True, - console: Optional[Console] = None, - transient: bool = False, - get_time: Optional[GetTimeCallable] = None, - refresh_per_second: float = 10, - style: StyleType = "bar.back", - complete_style: StyleType = "bar.complete", - finished_style: StyleType = "bar.finished", - pulse_style: StyleType = "bar.pulse", - update_period: float = 0.1, - disable: bool = False, - show_speed: bool = True, - ) -> Iterable[ProgressType]: - """ - Track progress of an iterable. - - Args: - sequence: Iterable to track - task_id: Existing task ID or None for new task - description: Task description - total: Total items or None to auto-detect - auto_refresh: Enable automatic refresh - console: Console for output - transient: Remove when complete - get_time: Time function - refresh_per_second: Refresh rate - style: Progress bar style - complete_style: Completed portion style - finished_style: Finished bar style - pulse_style: Indeterminate pulse style - update_period: Update frequency - disable: Disable progress display - show_speed: Show processing speed - - Returns: - Iterator that yields items while tracking progress - """ - - @contextmanager - def wrap_file( - self, - file: BinaryIO, - total: int, - *, - task_id: Optional[TaskID] = None, - description: str = "Reading...", - ) -> Iterator[BinaryIO]: - """ - Wrap a file for progress tracking. - - Args: - file: File object to wrap - total: Total file size in bytes - task_id: Existing task ID or None for new task - description: Task description - - Yields: - Wrapped file object that updates progress - """ - - @contextmanager - def open( - self, - file: Union[str, "PathLike[str]", IO[bytes]], - mode: str = "rb", - *, - buffering: int = -1, - encoding: Optional[str] = None, - errors: Optional[str] = None, - newline: Optional[str] = None, - description: str = "Reading...", - auto_refresh: bool = True, - console: Optional[Console] = None, - transient: bool = False, - get_time: Optional[GetTimeCallable] = None, - refresh_per_second: float = 10, - style: StyleType = "bar.back", - complete_style: StyleType = "bar.complete", - finished_style: StyleType = "bar.finished", - pulse_style: StyleType = "bar.pulse", - disable: bool = False, - ) -> Iterator[IO[Any]]: - """ - Open a file with progress tracking. - - Args: - file: File path or file object - mode: File open mode - buffering: Buffer size - encoding: Text encoding - errors: Error handling - newline: Newline handling - description: Task description - auto_refresh: Enable automatic refresh - console: Console for output - transient: Remove when complete - get_time: Time function - refresh_per_second: Refresh rate - style: Progress bar style - complete_style: Completed portion style - finished_style: Finished bar style - pulse_style: Indeterminate pulse style - disable: Disable progress display - - Yields: - File object with progress tracking - """ - - # Properties - @property - def tasks(self) -> List[Task]: - """Get list of all tasks.""" - - @property - def task_ids(self) -> List[TaskID]: - """Get list of all task IDs.""" - - @property - def finished(self) -> bool: - """Check if all tasks are finished.""" - - @property - def live(self) -> Live: - """Get the underlying Live display.""" -``` - -### Task Class - -Task information and state. - -```python { .api } -class Task: - """ - Progress task data and state. - - Attributes: - id: Unique task identifier - description: Task description text - total: Total amount of work or None for indeterminate - completed: Amount of work completed - visible: Whether task is visible in display - fields: Custom data fields - created_time: Time when task was created - started_time: Time when task was started - stopped_time: Time when task was stopped - finished_time: Time when task finished - """ - id: TaskID - description: str - total: Optional[float] - completed: float - visible: bool - fields: Dict[str, Any] - created_time: float - started_time: Optional[float] - stopped_time: Optional[float] - finished_time: Optional[float] - - @property - def elapsed(self) -> Optional[float]: - """Get elapsed time since task started.""" - - @property - def finished(self) -> bool: - """Check if task is finished.""" - - @property - def percentage(self) -> float: - """Get completion percentage (0-100).""" - - @property - def speed(self) -> Optional[float]: - """Get current processing speed.""" - - @property - def time_remaining(self) -> Optional[float]: - """Get estimated time remaining.""" -``` - -### Progress Columns - -Customizable display columns for progress bars. - -```python { .api } -class ProgressColumn(ABC): - """Base class for progress display columns.""" - - @abstractmethod - def render(self, task: Task) -> RenderableType: - """ - Render column content for a task. - - Args: - task: Task to render - - Returns: - Renderable content - """ - -class RenderableColumn(ProgressColumn): - """Column that displays a fixed renderable.""" - - def __init__(self, renderable: RenderableType, *, table_column: Optional[Column] = None): ... - -class SpinnerColumn(ProgressColumn): - """Column with animated spinner.""" - - def __init__( - self, - spinner_name: str = "dots", - style: Optional[StyleType] = None, - speed: float = 1.0, - finished_text: TextType = "✓", - table_column: Optional[Column] = None, - ): ... - -class TextColumn(ProgressColumn): - """Column displaying task text/description.""" - - def __init__( - self, - text_format: str = "[progress.description]{task.description}", - style: StyleType = "", - justify: JustifyMethod = "left", - markup: bool = True, - highlighter: Optional[Highlighter] = None, - table_column: Optional[Column] = None, - ): ... - -class BarColumn(ProgressColumn): - """Progress bar column.""" - - def __init__( - self, - bar_width: Optional[int] = 40, - style: StyleType = "bar.back", - complete_style: StyleType = "bar.complete", - finished_style: StyleType = "bar.finished", - pulse_style: StyleType = "bar.pulse", - table_column: Optional[Column] = None, - ): ... - -class TaskProgressColumn(TextColumn): - """Column showing task progress percentage.""" - - def __init__( - self, - text_format: str = "[progress.percentage]{task.percentage:>3.1f}%", - style: StyleType = "", - justify: JustifyMethod = "left", - markup: bool = True, - highlighter: Optional[Highlighter] = None, - table_column: Optional[Column] = None, - ): ... - -class TimeElapsedColumn(ProgressColumn): - """Column showing elapsed time.""" - - def __init__(self, table_column: Optional[Column] = None): ... - -class TimeRemainingColumn(ProgressColumn): - """Column showing estimated time remaining.""" - - def __init__( - self, - compact: bool = False, - elapsed_when_finished: bool = False, - table_column: Optional[Column] = None, - ): ... - -class FileSizeColumn(ProgressColumn): - """Column showing file size completed.""" - - def __init__(self, table_column: Optional[Column] = None): ... - -class TotalFileSizeColumn(ProgressColumn): - """Column showing total file size.""" - - def __init__(self, table_column: Optional[Column] = None): ... - -class MofNCompleteColumn(ProgressColumn): - """Column showing 'M of N' completion.""" - - def __init__( - self, - separator: str = "/", - table_column: Optional[Column] = None, - ): ... - -class DownloadColumn(ProgressColumn): - """Column for download progress with speed.""" - - def __init__( - self, - binary_units: bool = False, - table_column: Optional[Column] = None, - ): ... - -class TransferSpeedColumn(ProgressColumn): - """Column showing transfer speed.""" - - def __init__(self, table_column: Optional[Column] = None): ... -``` - -**Usage Examples:** - -```python -from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn, TimeElapsedColumn -import time - -# Basic progress bar -with Progress() as progress: - task = progress.add_task("Processing...", total=100) - - for i in range(100): - time.sleep(0.1) - progress.update(task, advance=1) - -# Custom progress layout -progress = Progress( - SpinnerColumn(), - TextColumn("[progress.description]{task.description}"), - BarColumn(), - TaskProgressColumn(), - TimeElapsedColumn(), -) - -with progress: - task1 = progress.add_task("Download", total=1000) - task2 = progress.add_task("Process", total=500) - - # Simulate work - for i in range(1000): - time.sleep(0.01) - progress.update(task1, advance=1) - if i % 2 == 0: - progress.update(task2, advance=1) - -# Multiple concurrent tasks -with Progress() as progress: - tasks = [ - progress.add_task("Task 1", total=100), - progress.add_task("Task 2", total=200), - progress.add_task("Task 3", total=150), - ] - - # Process tasks concurrently - import threading - - def work(task_id, total): - for i in range(total): - time.sleep(0.05) - progress.update(task_id, advance=1) - - threads = [] - for task_id, total in zip(tasks, [100, 200, 150]): - thread = threading.Thread(target=work, args=(task_id, total)) - threads.append(thread) - thread.start() - - for thread in threads: - thread.join() - -# Track iterable -from rich.progress import track - -items = range(100) -for item in track(items, description="Processing items..."): - time.sleep(0.1) # Simulate work - -# File download simulation -from rich.progress import Progress, DownloadColumn, TransferSpeedColumn - -def download_file(url, size): - """Simulate file download.""" - progress = Progress( - TextColumn("[bold blue]{task.description}", justify="right"), - BarColumn(bar_width=None), - "[progress.percentage]{task.percentage:>3.1f}%", - "•", - DownloadColumn(), - "•", - TransferSpeedColumn(), - "•", - TimeElapsedColumn(), - ) - - with progress: - task = progress.add_task("download", filename=url, total=size) - for chunk_size in [500, 600, 300, 400, 800, 200]: - time.sleep(0.5) # Simulate network delay - progress.update(task, advance=chunk_size) - -# Example: download_file("example.zip", 3000) - -# Indeterminate progress (no known total) -with Progress() as progress: - task = progress.add_task("Scanning...", total=None) - - for i in range(50): - time.sleep(0.1) - progress.update(task, advance=1) # Still advances for timing - -# Progress with custom fields -with Progress() as progress: - task = progress.add_task("Custom Task", total=100, status="Starting") - - for i in range(100): - status = f"Processing item {i+1}" - progress.update(task, advance=1, status=status) - time.sleep(0.05) - -# File processing with progress -from rich.progress import Progress - -def process_files(file_paths): - with Progress() as progress: - main_task = progress.add_task("Processing files", total=len(file_paths)) - - for file_path in file_paths: - # Process individual file - file_task = progress.add_task(f"Processing {file_path}", total=100) - - for chunk in range(100): - time.sleep(0.01) # Simulate file processing - progress.update(file_task, advance=1) - - progress.update(main_task, advance=1) - progress.remove_task(file_task) - -# Example: process_files(["file1.txt", "file2.txt", "file3.txt"]) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/syntax.md b/.tessl/tiles/tessl/pypi-rich/docs/syntax.md deleted file mode 100644 index 8e45d46cd..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/syntax.md +++ /dev/null @@ -1,411 +0,0 @@ -# Syntax Highlighting - -Code syntax highlighting with theme support and extensive language coverage via Pygments. Rich provides comprehensive syntax highlighting capabilities for displaying source code with proper formatting and color schemes. - -## Capabilities - -### Syntax Class - -Main syntax highlighting component with theme and formatting options. - -```python { .api } -class Syntax: - """ - Syntax highlighted code display. - - Args: - code: Source code to highlight - lexer: Pygments lexer name or instance - theme: Color theme name or SyntaxTheme instance - dedent: Remove common leading whitespace - line_numbers: Show line numbers - start_line: Starting line number - line_range: Range of lines to highlight (start, end) - highlight_lines: Set of line numbers to highlight - code_width: Fixed width for code or None for auto - tab_size: Size of tab characters in spaces - word_wrap: Enable word wrapping - background_color: Override background color - indent_guides: Show indentation guides - padding: Padding around code block - """ - def __init__( - self, - code: str, - lexer: Optional[Union[Lexer, str]] = None, - *, - theme: Union[str, SyntaxTheme] = "monokai", - dedent: bool = False, - line_numbers: bool = False, - start_line: int = 1, - line_range: Optional[Tuple[int, int]] = None, - highlight_lines: Optional[Set[int]] = None, - code_width: Optional[int] = None, - tab_size: int = 4, - word_wrap: bool = False, - background_color: Optional[str] = None, - indent_guides: bool = False, - padding: PaddingDimensions = 0, - ): ... - - @classmethod - def from_path( - cls, - path: Union[str, PathLike[str]], - encoding: str = "utf-8", - lexer: Optional[Union[Lexer, str]] = None, - theme: Union[str, SyntaxTheme] = "monokai", - dedent: bool = False, - line_numbers: bool = False, - line_range: Optional[Tuple[int, int]] = None, - start_line: int = 1, - highlight_lines: Optional[Set[int]] = None, - code_width: Optional[int] = None, - tab_size: int = 4, - word_wrap: bool = False, - background_color: Optional[str] = None, - indent_guides: bool = False, - padding: PaddingDimensions = 0, - ) -> "Syntax": - """ - Create Syntax from file path. - - Args: - path: Path to source code file - encoding: File encoding - lexer: Lexer name or None to auto-detect - theme: Syntax theme - dedent: Remove common indentation - line_numbers: Show line numbers - line_range: Range of lines to show - start_line: Starting line number - highlight_lines: Lines to highlight - code_width: Fixed code width - tab_size: Tab size in spaces - word_wrap: Enable word wrapping - background_color: Background color override - indent_guides: Show indentation guides - padding: Code block padding - - Returns: - New Syntax instance with file contents - """ - - @classmethod - def guess_lexer(cls, path: Union[str, PathLike[str]], code: str) -> str: - """ - Guess lexer from file path and content. - - Args: - path: File path for extension hints - code: Source code content - - Returns: - Lexer name for the code - """ - - def highlight(self, code: str) -> Text: - """ - Apply syntax highlighting to code. - - Args: - code: Source code to highlight - - Returns: - Highlighted text object - """ - - # Properties - @property - def lexer(self) -> Optional[Lexer]: - """Get the Pygments lexer.""" - - @property - def theme(self) -> SyntaxTheme: - """Get the syntax theme.""" -``` - -### Syntax Themes - -Theme system for syntax highlighting colors. - -```python { .api } -class SyntaxTheme(ABC): - """Abstract base for syntax highlighting themes.""" - - @abstractmethod - def get_style_for_token(self, token_type: TokenType) -> Style: - """ - Get style for a token type. - - Args: - token_type: Pygments token type - - Returns: - Rich Style for the token - """ - - @abstractmethod - def get_background_style(self) -> Style: - """ - Get background style for code blocks. - - Returns: - Rich Style for background - """ - -class PygmentsSyntaxTheme(SyntaxTheme): - """Syntax theme based on Pygments themes.""" - - def __init__( - self, - theme: Union[str, Type[PygmentsStyle]] - ): ... - - @classmethod - def get_theme_names(cls) -> List[str]: - """ - Get list of available Pygments theme names. - - Returns: - List of theme names - """ - -class ANSISyntaxTheme(SyntaxTheme): - """Syntax theme using ANSI color codes.""" - - def __init__(self, style_map: Dict[TokenType, Style]): ... - -# Default themes -DEFAULT_THEME: str # Default theme name -RICH_SYNTAX_THEMES: Dict[str, SyntaxTheme] # Available themes -``` - -**Usage Examples:** - -```python -from rich.console import Console -from rich.syntax import Syntax -import tempfile -import os - -console = Console() - -# Basic syntax highlighting -python_code = ''' -def fibonacci(n): - """Generate Fibonacci sequence up to n.""" - a, b = 0, 1 - while a < n: - print(a, end=' ') - a, b = b, a + b - print() - -fibonacci(100) -''' - -syntax = Syntax(python_code, "python") -console.print(syntax) - -# Syntax with line numbers -syntax = Syntax( - python_code, - "python", - line_numbers=True, - start_line=1 -) -console.print(syntax) - -# Highlight specific lines -syntax = Syntax( - python_code, - "python", - line_numbers=True, - highlight_lines={2, 4, 5} # Highlight lines 2, 4, and 5 -) -console.print(syntax) - -# Different themes -themes = ["monokai", "github-dark", "one-dark", "solarized-light"] -for theme in themes: - console.print(f"\n[bold]Theme: {theme}[/bold]") - syntax = Syntax(python_code, "python", theme=theme, line_numbers=True) - console.print(syntax) - -# Load from file -# Create temporary file -with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: - f.write(python_code) - temp_path = f.name - -try: - # Load and highlight from file - syntax = Syntax.from_path( - temp_path, - line_numbers=True, - theme="monokai" - ) - console.print(syntax) -finally: - os.unlink(temp_path) - -# JavaScript example -js_code = ''' -function quickSort(arr) { - if (arr.length <= 1) { - return arr; - } - - const pivot = arr[Math.floor(arr.length / 2)]; - const left = arr.filter(x => x < pivot); - const middle = arr.filter(x => x === pivot); - const right = arr.filter(x => x > pivot); - - return [...quickSort(left), ...middle, ...quickSort(right)]; -} - -const numbers = [64, 34, 25, 12, 22, 11, 90]; -console.log(quickSort(numbers)); -''' - -syntax = Syntax(js_code, "javascript", theme="github-dark", line_numbers=True) -console.print(syntax) - -# SQL example with custom settings -sql_code = ''' -SELECT - customers.name, - orders.order_date, - products.product_name, - order_items.quantity * products.price AS total_price -FROM customers -JOIN orders ON customers.customer_id = orders.customer_id -JOIN order_items ON orders.order_id = order_items.order_id -JOIN products ON order_items.product_id = products.product_id -WHERE orders.order_date >= '2024-01-01' -ORDER BY orders.order_date DESC, total_price DESC; -''' - -syntax = Syntax( - sql_code, - "sql", - theme="one-dark", - line_numbers=True, - word_wrap=True, - indent_guides=True -) -console.print(syntax) - -# Show line range -long_code = ''' -import os -import sys -import json -import requests -from datetime import datetime -from typing import List, Dict, Optional - -class DataProcessor: - def __init__(self, config_path: str): - self.config = self.load_config(config_path) - self.session = requests.Session() - - def load_config(self, path: str) -> Dict: - with open(path, 'r') as f: - return json.load(f) - - def process_data(self, data: List[Dict]) -> List[Dict]: - results = [] - for item in data: - processed = self.transform_item(item) - if self.validate_item(processed): - results.append(processed) - return results - - def transform_item(self, item: Dict) -> Dict: - # Transform logic here - return { - 'id': item.get('id'), - 'timestamp': datetime.now().isoformat(), - 'value': item.get('value', 0) * 2 - } - - def validate_item(self, item: Dict) -> bool: - return all(key in item for key in ['id', 'timestamp', 'value']) -''' - -# Show only lines 10-20 -syntax = Syntax( - long_code, - "python", - line_numbers=True, - line_range=(10, 20), - start_line=10, - theme="monokai" -) -console.print(syntax) - -# Code with custom background -syntax = Syntax( - python_code, - "python", - theme="github-dark", - background_color="black", - padding=1 -) -console.print(syntax) - -# Dedent code (remove common indentation) -indented_code = ''' - def helper_function(): - print("This is indented") - return True - - def another_function(): - if helper_function(): - print("Success!") -''' - -syntax = Syntax( - indented_code, - "python", - dedent=True, - line_numbers=True -) -console.print(syntax) - -# Auto-detect lexer from filename -def highlight_file_content(filename: str, content: str): - """Highlight code with auto-detected lexer.""" - lexer = Syntax.guess_lexer(filename, content) - syntax = Syntax(content, lexer, line_numbers=True) - console.print(f"[bold]File: {filename} (Lexer: {lexer})[/bold]") - console.print(syntax) - -# Examples of auto-detection -highlight_file_content("script.py", "print('Hello, World!')") -highlight_file_content("style.css", "body { color: red; }") -highlight_file_content("config.json", '{"name": "example", "value": 42}') - -# Syntax in panels -from rich.panel import Panel - -code_panel = Panel( - Syntax(python_code, "python", theme="monokai"), - title="Python Code", - border_style="blue" -) -console.print(code_panel) - -# Multiple code blocks in columns -from rich.columns import Columns - -python_syntax = Syntax("print('Python')", "python", theme="github-dark") -js_syntax = Syntax("console.log('JavaScript');", "javascript", theme="github-dark") - -columns = Columns([ - Panel(python_syntax, title="Python"), - Panel(js_syntax, title="JavaScript") -]) -console.print(columns) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/tables.md b/.tessl/tiles/tessl/pypi-rich/docs/tables.md deleted file mode 100644 index 19083a7ef..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/tables.md +++ /dev/null @@ -1,378 +0,0 @@ -# Tables - -Comprehensive table system with flexible columns, styling, borders, and automatic sizing. Rich tables support complex layouts, custom formatting, and responsive design with automatic width calculation. - -## Capabilities - -### Table Class - -Advanced table rendering with comprehensive formatting and layout options. - -```python { .api } -class Table: - """ - Renderable table with automatic sizing and formatting. - - Args: - *columns: Column definitions or header strings - title: Title displayed above the table - caption: Caption displayed below the table - width: Fixed table width or None for automatic - min_width: Minimum table width - box: Box style for borders - safe_box: Use safe box characters for compatibility - padding: Cell padding (top, right, bottom, left) - collapse_padding: Collapse padding between cells - pad_edge: Add padding to table edges - expand: Expand table to fill available width - show_header: Display header row - show_footer: Display footer row - show_edge: Display outer borders - show_lines: Display lines between rows - leading: Number of blank lines between rows - style: Default table style - row_styles: Alternating row styles - header_style: Header row style - footer_style: Footer row style - border_style: Border style - title_style: Title style - caption_style: Caption style - title_justify: Title justification - caption_justify: Caption justification - highlight: Enable cell content highlighting - """ - def __init__( - self, - *columns: Union[Column, str], - title: Optional[TextType] = None, - caption: Optional[TextType] = None, - width: Optional[int] = None, - min_width: Optional[int] = None, - box: Optional[Box] = box.HEAVY_HEAD, - safe_box: Optional[bool] = None, - padding: PaddingDimensions = (0, 1), - collapse_padding: bool = False, - pad_edge: bool = True, - expand: bool = False, - show_header: bool = True, - show_footer: bool = False, - show_edge: bool = True, - show_lines: bool = False, - leading: int = 0, - style: StyleType = "", - row_styles: Optional[Iterable[StyleType]] = None, - header_style: Optional[StyleType] = None, - footer_style: Optional[StyleType] = None, - border_style: Optional[StyleType] = None, - title_style: Optional[StyleType] = None, - caption_style: Optional[StyleType] = None, - title_justify: JustifyMethod = "center", - caption_justify: JustifyMethod = "center", - highlight: bool = False, - ): ... - - def add_column( - self, - header: RenderableType = "", - *, - footer: RenderableType = "", - header_style: Optional[StyleType] = None, - footer_style: Optional[StyleType] = None, - style: Optional[StyleType] = None, - justify: JustifyMethod = "left", - vertical: VerticalAlignMethod = "top", - overflow: OverflowMethod = "ellipsis", - width: Optional[int] = None, - min_width: Optional[int] = None, - max_width: Optional[int] = None, - ratio: Optional[int] = None, - no_wrap: bool = False, - ) -> None: - """ - Add a column to the table. - - Args: - header: Column header content - footer: Column footer content - header_style: Style for header cell - footer_style: Style for footer cell - style: Default style for column cells - justify: Text justification in column - vertical: Vertical alignment in column - overflow: Overflow handling method - width: Fixed column width - min_width: Minimum column width - max_width: Maximum column width - ratio: Column width ratio for flexible sizing - no_wrap: Disable text wrapping in column - """ - - def add_row( - self, - *renderables: Optional[RenderableType], - style: Optional[StyleType] = None, - end_section: bool = False, - ) -> None: - """ - Add a row to the table. - - Args: - *renderables: Cell contents for each column - style: Style for the entire row - end_section: Mark this row as end of a section - """ - - def add_section(self) -> None: - """Add a section break (line) after the current row.""" - - @classmethod - def grid( - cls, - *, - padding: PaddingDimensions = 0, - collapse_padding: bool = True, - pad_edge: bool = False, - expand: bool = False, - ) -> "Table": - """ - Create a table configured as a grid (no borders). - - Args: - padding: Cell padding - collapse_padding: Collapse padding between cells - pad_edge: Add padding to grid edges - expand: Expand grid to fill available width - - Returns: - New Table configured as grid - """ - - # Properties - @property - def columns(self) -> List[Column]: - """Get list of table columns.""" - - @property - def rows(self) -> List[Row]: - """Get list of table rows.""" - - @property - def row_count(self) -> int: - """Get number of rows in table.""" -``` - -### Column Class - -Column definition with formatting and sizing options. - -```python { .api } -@dataclass -class Column: - """ - Table column definition. - - Attributes: - header: Header content for the column - footer: Footer content for the column - header_style: Style for header cell - footer_style: Style for footer cell - style: Default style for column cells - justify: Text justification method - vertical: Vertical alignment method - overflow: Overflow handling method - width: Fixed column width - min_width: Minimum column width - max_width: Maximum column width - ratio: Width ratio for flexible sizing - no_wrap: Disable text wrapping - """ - header: RenderableType = "" - footer: RenderableType = "" - header_style: StyleType = "" - footer_style: StyleType = "" - style: StyleType = "" - justify: JustifyMethod = "left" - vertical: VerticalAlignMethod = "top" - overflow: OverflowMethod = "ellipsis" - width: Optional[int] = None - min_width: Optional[int] = None - max_width: Optional[int] = None - ratio: Optional[int] = None - no_wrap: bool = False -``` - -### Row Class - -Table row data container. - -```python { .api } -@dataclass -class Row: - """ - Table row data. - - Attributes: - style: Style for the entire row - end_section: Whether this row ends a section - """ - style: Optional[StyleType] = None - end_section: bool = False -``` - -### Box Styles - -Predefined box drawing styles for table borders. - -```python { .api } -class Box: - """Box drawing character set for table borders.""" - - def __init__( - self, - box: str, - *, - ascii: bool = False, - ): ... - -# Predefined box styles -ASCII: Box # ASCII characters only -SQUARE: Box # Square corners -MINIMAL: Box # Minimal borders -SIMPLE: Box # Simple lines -SIMPLE_HEAD: Box # Simple with header separator -SIMPLE_HEAVY: Box # Simple with heavy lines -HORIZONTALS: Box # Horizontal lines only -ROUNDED: Box # Rounded corners -HEAVY: Box # Heavy lines -HEAVY_EDGE: Box # Heavy outer edge -HEAVY_HEAD: Box # Heavy header separator -DOUBLE: Box # Double lines -DOUBLE_EDGE: Box # Double outer edge -``` - -**Usage Examples:** - -```python -from rich.console import Console -from rich.table import Table, Column -from rich import box - -console = Console() - -# Basic table -table = Table() -table.add_column("Name") -table.add_column("Age") -table.add_column("City") - -table.add_row("Alice", "25", "New York") -table.add_row("Bob", "30", "London") -table.add_row("Charlie", "35", "Tokyo") - -console.print(table) - -# Styled table with title and caption -table = Table( - title="Employee Directory", - caption="Last updated: 2024-01-01", - box=box.ROUNDED, - title_style="bold blue", - caption_style="italic dim" -) - -table.add_column("ID", justify="center", style="cyan", no_wrap=True) -table.add_column("Name", style="magenta") -table.add_column("Department", justify="right", style="green") -table.add_column("Salary", justify="right", style="yellow") - -table.add_row("001", "John Doe", "Engineering", "$75,000") -table.add_row("002", "Jane Smith", "Marketing", "$65,000") -table.add_row("003", "Bob Johnson", "Sales", "$55,000") - -console.print(table) - -# Table with column configuration -table = Table(show_header=True, header_style="bold red") - -# Add columns with specific formatting -table.add_column( - "Product", - header_style="bold blue", - style="cyan", - min_width=10, - max_width=20 -) -table.add_column( - "Price", - justify="right", - style="green", - header_style="bold green" -) -table.add_column( - "Status", - justify="center", - style="yellow", - vertical="middle" -) - -table.add_row("Laptop", "$999.99", "In Stock") -table.add_row("Mouse", "$29.99", "Out of Stock") -table.add_row("Keyboard", "$79.99", "In Stock") - -console.print(table) - -# Grid layout (no borders) -grid = Table.grid(padding=1) -grid.add_column(style="red", justify="right") -grid.add_column(style="blue") -grid.add_column(style="green") - -grid.add_row("Label 1:", "Value A", "Extra Info") -grid.add_row("Label 2:", "Value B", "More Info") -grid.add_row("Label 3:", "Value C", "Additional") - -console.print(grid) - -# Table with sections -table = Table() -table.add_column("Category") -table.add_column("Item") -table.add_column("Count") - -table.add_row("Fruits", "Apples", "10") -table.add_row("Fruits", "Oranges", "15") -table.add_section() # Add section break - -table.add_row("Vegetables", "Carrots", "8") -table.add_row("Vegetables", "Lettuce", "12") - -console.print(table) - -# Responsive table with ratios -table = Table(expand=True) -table.add_column("Description", ratio=3) # 3/4 of available width -table.add_column("Value", ratio=1, justify="right") # 1/4 of width - -table.add_row("Very long description that will wrap", "100") -table.add_row("Short desc", "200") - -console.print(table) - -# Table with rich content -from rich.text import Text -from rich.panel import Panel - -table = Table() -table.add_column("Rich Content") -table.add_column("Status") - -# Add styled text -styled_text = Text("Important Item", style="bold red") -table.add_row(styled_text, "Active") - -# Add panel as cell content -panel = Panel("Wrapped Content", style="blue") -table.add_row(panel, "Pending") - -console.print(table) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/text-styling.md b/.tessl/tiles/tessl/pypi-rich/docs/text-styling.md deleted file mode 100644 index de7acf168..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/text-styling.md +++ /dev/null @@ -1,739 +0,0 @@ -# Text and Styling - -Advanced text objects with precise styling control, markup support, and comprehensive formatting options. Rich provides powerful text handling with styled spans, color support, and flexible rendering capabilities. - -## Capabilities - -### Text Class - -Rich text objects with styling spans and advanced formatting capabilities. - -```python { .api } -class Text: - """ - Styled text with Rich formatting capabilities. - - Args: - text: Plain text content - style: Default style for the text - justify: Text justification method - overflow: Overflow handling method - no_wrap: Disable text wrapping - end: String to append at end - tab_size: Size of tab characters - spans: List of styled spans - """ - def __init__( - self, - text: str = "", - style: StyleType = "", - *, - justify: Optional[JustifyMethod] = None, - overflow: OverflowMethod = "fold", - no_wrap: Optional[bool] = None, - end: str = "\n", - tab_size: Optional[int] = 8, - spans: Optional[List[Span]] = None, - ): ... - - def stylize( - self, - style: StyleType, - start: int = 0, - end: Optional[int] = None, - ) -> None: - """ - Apply style to a range of text. - - Args: - style: Style to apply - start: Start character index - end: End character index or None for end of text - """ - - def stylize_range( - self, - style: StyleType, - start: int = 0, - end: Optional[int] = None, - ) -> None: - """ - Apply style to a range of text (alias for stylize). - - Args: - style: Style to apply - start: Start character index - end: End character index or None for end of text - """ - - def stylize_before( - self, - style: StyleType, - start: int = 0, - end: Optional[int] = None, - ) -> "Text": - """ - Apply style before existing styles in a range. - - Args: - style: Style to apply - start: Start character index - end: End character index or None for end of text - - Returns: - New Text instance with applied style - """ - - def stylize_after( - self, - style: StyleType, - start: int = 0, - end: Optional[int] = None, - ) -> "Text": - """ - Apply style after existing styles in a range. - - Args: - style: Style to apply - start: Start character index - end: End character index or None for end of text - - Returns: - New Text instance with applied style - """ - - def highlight_words( - self, - words: Iterable[str], - style: StyleType, - *, - case_sensitive: bool = True, - ) -> int: - """ - Highlight specific words in the text. - - Args: - words: Words to highlight - style: Style to apply to highlighted words - case_sensitive: Enable case-sensitive matching - - Returns: - Number of words highlighted - """ - - def highlight_regex( - self, - re_highlight: Union[str, Pattern[str]], - style: Optional[StyleType] = None, - *, - style_prefix: str = "", - ) -> int: - """ - Highlight text matching a regular expression. - - Args: - re_highlight: Regular expression pattern - style: Style to apply or None to use groups - style_prefix: Prefix for group-based style names - - Returns: - Number of matches highlighted - """ - - def append( - self, - text: Union[str, "Text"], - style: Optional[StyleType] = None, - ) -> "Text": - """ - Append text with optional styling. - - Args: - text: Text to append - style: Style to apply to appended text - - Returns: - Self for method chaining - """ - - def copy(self) -> "Text": - """ - Create a copy of this text object. - - Returns: - New Text instance with same content and styles - """ - - def copy_styles(self, text: "Text") -> None: - """ - Copy styles from another text object. - - Args: - text: Text object to copy styles from - """ - - def split( - self, - separator: str = "\n", - *, - include_separator: bool = False, - allow_blank: bool = True, - ) -> List["Text"]: - """ - Split text by separator. - - Args: - separator: String to split on - include_separator: Include separator in results - allow_blank: Allow blank strings in results - - Returns: - List of Text objects - """ - - def divide( - self, offsets: Iterable[int] - ) -> List["Text"]: - """ - Divide text at specific character offsets. - - Args: - offsets: Character positions to split at - - Returns: - List of Text objects - """ - - def right_crop(self, amount: int = 1) -> "Text": - """ - Remove characters from the right. - - Args: - amount: Number of characters to remove - - Returns: - New Text with characters removed - """ - - def truncate( - self, - max_width: int, - *, - overflow: OverflowMethod = "fold", - pad: bool = False, - ) -> None: - """ - Truncate text to maximum width. - - Args: - max_width: Maximum width in characters - overflow: Overflow handling method - pad: Pad to exact width with spaces - """ - - def pad_left(self, count: int, character: str = " ") -> "Text": - """ - Add padding to the left. - - Args: - count: Number of characters to add - character: Character to use for padding - - Returns: - New Text with padding - """ - - def pad_right(self, count: int, character: str = " ") -> "Text": - """ - Add padding to the right. - - Args: - count: Number of characters to add - character: Character to use for padding - - Returns: - New Text with padding - """ - - def align(self, align: AlignMethod, width: int, character: str = " ") -> "Text": - """ - Align text within a given width. - - Args: - align: Alignment method - width: Total width - character: Character to use for padding - - Returns: - New aligned Text - """ - - def strip(self, characters: Optional[str] = None) -> "Text": - """ - Strip characters from both ends. - - Args: - characters: Characters to strip or None for whitespace - - Returns: - New Text with characters stripped - """ - - def rstrip(self, characters: Optional[str] = None) -> "Text": - """ - Strip characters from the right end. - - Args: - characters: Characters to strip or None for whitespace - - Returns: - New Text with characters stripped - """ - - def lstrip(self, characters: Optional[str] = None) -> "Text": - """ - Strip characters from the left end. - - Args: - characters: Characters to strip or None for whitespace - - Returns: - New Text with characters stripped - """ - - def expand_tabs(self, tab_size: Optional[int] = None) -> "Text": - """ - Expand tab characters to spaces. - - Args: - tab_size: Size of tabs or None to use default - - Returns: - New Text with tabs expanded - """ - - def replace( - self, - old: str, - new: Union[str, "Text"], - count: int = -1, - ) -> "Text": - """ - Replace occurrences of a substring. - - Args: - old: Substring to replace - new: Replacement text - count: Maximum replacements or -1 for all - - Returns: - New Text with replacements - """ - - @classmethod - def from_markup( - cls, - text: str, - *, - style: StyleType = "", - emoji: bool = True, - emoji_variant: Optional[EmojiVariant] = None, - ) -> "Text": - """ - Create Text from Rich markup. - - Args: - text: Text with Rich markup tags - style: Base style to apply - emoji: Enable emoji rendering - emoji_variant: Emoji variant preference - - Returns: - New Text object with markup applied - """ - - @classmethod - def from_ansi( - cls, - text: str, - *, - style: StyleType = "", - no_color: Optional[bool] = None, - ) -> "Text": - """ - Create Text from ANSI escaped string. - - Args: - text: Text with ANSI escape codes - style: Base style to apply - no_color: Disable color processing - - Returns: - New Text object with ANSI styles converted - """ - - @classmethod - def styled( - cls, - text: str, - style: StyleType, - ) -> "Text": - """ - Create styled Text. - - Args: - text: Plain text - style: Style to apply - - Returns: - New styled Text object - """ - - @classmethod - def assemble( - cls, - *parts: Union[str, "Text", Tuple[str, StyleType]], - style: StyleType = "", - justify: Optional[JustifyMethod] = None, - overflow: OverflowMethod = "fold", - no_wrap: Optional[bool] = None, - end: str = "\n", - tab_size: int = 8, - meta: Optional[Dict[str, Any]] = None, - ) -> "Text": - """ - Assemble Text from multiple parts. - - Args: - *parts: Text parts as strings, Text objects, or (text, style) tuples - style: Base style - justify: Text justification - overflow: Overflow handling - no_wrap: Disable wrapping - end: End string - tab_size: Tab size - meta: Metadata dictionary - - Returns: - New assembled Text object - """ - - # Properties - @property - def plain(self) -> str: - """Get plain text without styling.""" - - @property - def markup(self) -> str: - """Get text as Rich markup.""" - - @property - def spans(self) -> List[Span]: - """Get list of styled spans.""" - - @property - def style(self) -> Style: - """Get base style.""" -``` - -### Span Class - -Represents a styled region within text. - -```python { .api } -class Span(NamedTuple): - """ - A styled span within text. - - Attributes: - start: Start character index - end: End character index - style: Style for this span - """ - start: int - end: int - style: Union[str, Style] - - def split(self, offset: int) -> Tuple["Span", Optional["Span"]]: - """ - Split span at offset. - - Args: - offset: Character offset to split at - - Returns: - Tuple of (left_span, right_span or None) - """ - - def move(self, offset: int) -> "Span": - """ - Move span by offset. - - Args: - offset: Number of characters to move - - Returns: - New span with adjusted position - """ - - def right_crop(self, offset: int) -> "Span": - """ - Crop span at offset. - - Args: - offset: Offset to crop at - - Returns: - New cropped span - """ -``` - -### Style Class - -Comprehensive styling system with support for colors, typography, and effects. - -```python { .api } -class Style: - """ - Immutable style definition for text formatting. - - Args: - color: Text color - bgcolor: Background color - bold: Bold text - dim: Dim text - italic: Italic text - underline: Underlined text - blink: Blinking text - blink2: Fast blinking text - reverse: Reverse video - conceal: Concealed text - strike: Strikethrough text - underline2: Double underline - frame: Framed text - encircle: Encircled text - overline: Overlined text - link: Hyperlink URL - meta: Metadata dictionary - """ - def __init__( - self, - *, - color: Optional[ColorType] = None, - bgcolor: Optional[ColorType] = None, - bold: Optional[bool] = None, - dim: Optional[bool] = None, - italic: Optional[bool] = None, - underline: Optional[bool] = None, - blink: Optional[bool] = None, - blink2: Optional[bool] = None, - reverse: Optional[bool] = None, - conceal: Optional[bool] = None, - strike: Optional[bool] = None, - underline2: Optional[bool] = None, - frame: Optional[bool] = None, - encircle: Optional[bool] = None, - overline: Optional[bool] = None, - link: Optional[str] = None, - meta: Optional[Dict[str, Any]] = None, - ): ... - - def copy(self) -> "Style": - """ - Create a copy of this style. - - Returns: - New Style with same properties - """ - - def update(self, **kwargs: Any) -> "Style": - """ - Update style properties. - - Args: - **kwargs: Style properties to update - - Returns: - New Style with updated properties - """ - - def render( - self, - text: str = "", - *, - color_system: ColorSystem = ColorSystem.TRUECOLOR, - legacy_windows: bool = False, - ) -> str: - """ - Render style as ANSI codes. - - Args: - text: Text to wrap with codes - color_system: Target color system - legacy_windows: Enable legacy Windows compatibility - - Returns: - Text with ANSI escape codes - """ - - def test(self, text: Optional[str] = None) -> "Text": - """ - Create test text with this style applied. - - Args: - text: Test text or None for default - - Returns: - Styled Text object for testing - """ - - def without_color(self) -> "Style": - """ - Create style without color information. - - Returns: - New Style with colors removed - """ - - @classmethod - def parse(cls, style: Union[str, "Style"]) -> "Style": - """ - Parse style from string or return existing Style. - - Args: - style: Style string or Style object - - Returns: - Parsed Style object - """ - - @classmethod - def combine(cls, styles: Iterable["Style"]) -> "Style": - """ - Combine multiple styles. - - Args: - styles: Iterable of styles to combine - - Returns: - New combined Style - """ - - @classmethod - def chain(cls, *styles: "Style") -> "Style": - """ - Chain multiple styles together. - - Args: - *styles: Styles to chain - - Returns: - New chained Style - """ - - @classmethod - def null(cls) -> "Style": - """ - Create a null style with no formatting. - - Returns: - Empty Style object - """ - - # Properties - @property - def color(self) -> Optional[Color]: - """Text color.""" - - @property - def bgcolor(self) -> Optional[Color]: - """Background color.""" - - @property - def bold(self) -> Optional[bool]: - """Bold flag.""" - - @property - def dim(self) -> Optional[bool]: - """Dim flag.""" - - @property - def italic(self) -> Optional[bool]: - """Italic flag.""" - - @property - def underline(self) -> Optional[bool]: - """Underline flag.""" - - @property - def strike(self) -> Optional[bool]: - """Strikethrough flag.""" - - @property - def link(self) -> Optional[str]: - """Hyperlink URL.""" - - @property - def transparent_background(self) -> bool: - """Check if background is transparent.""" -``` - -**Usage Examples:** - -```python -from rich.text import Text, Span -from rich.style import Style -from rich.console import Console - -console = Console() - -# Basic text creation -text = Text("Hello World!") -text.stylize("bold red", 0, 5) # Style "Hello" -text.stylize("italic blue", 6, 12) # Style "World!" -console.print(text) - -# Text from markup -markup_text = Text.from_markup("[bold red]Error:[/bold red] Something went wrong") -console.print(markup_text) - -# Text assembly -assembled = Text.assemble( - "Status: ", - ("OK", "bold green"), - " - ", - ("Ready", "italic blue") -) -console.print(assembled) - -# Text with highlighting -code_text = Text("def hello_world():") -code_text.highlight_words(["def", "hello_world"], "bold blue") -console.print(code_text) - -# Advanced styling -style = Style( - color="red", - bgcolor="yellow", - bold=True, - italic=True, - underline=True -) -styled_text = Text("Important Notice", style=style) -console.print(styled_text) - -# Text manipulation -long_text = Text("This is a very long line of text that needs to be processed") -long_text.truncate(20, overflow="ellipsis") -console.print(long_text) - -# Working with spans -text = Text("Hello World") -spans = [ - Span(0, 5, "bold red"), - Span(6, 11, "italic blue") -] -for span in spans: - text.stylize(span.style, span.start, span.end) -console.print(text) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/docs/utilities.md b/.tessl/tiles/tessl/pypi-rich/docs/utilities.md deleted file mode 100644 index 2d551e01e..000000000 --- a/.tessl/tiles/tessl/pypi-rich/docs/utilities.md +++ /dev/null @@ -1,74 +0,0 @@ -# Utilities - -Color handling, measurement, error types, and other utility functions. Rich provides various utility functions and classes for working with colors, measurements, and other common operations. - -## Capabilities - -### Color System - -Comprehensive color handling and conversion. - -```python { .api } -class Color: - """Color representation with conversion capabilities.""" - - @classmethod - def parse(cls, color: ColorType) -> "Color": - """Parse color from various formats.""" - - def blend(self, destination: "Color", factor: float) -> "Color": - """Blend with another color.""" - - def get_truecolor(self) -> Tuple[int, int, int]: - """Get RGB values.""" - -class ColorTriplet(NamedTuple): - """RGB color triplet.""" - red: int - green: int - blue: int - -def inspect( - obj: Any, - *, - console: Optional[Console] = None, - title: Optional[str] = None, - help: bool = False, - methods: bool = False, - docs: bool = True, - private: bool = False, - dunder: bool = False, - sort: bool = True, - all: bool = False, - value: bool = True, -) -> None: - """Inspect Python objects with rich formatting.""" - -class Measurement(NamedTuple): - """Content measurement with min/max dimensions.""" - minimum: int - maximum: int - - def with_maximum(self, maximum: int) -> "Measurement": ... - def with_minimum(self, minimum: int) -> "Measurement": ... - def clamp(self, min_width: int, max_width: int) -> "Measurement": ... -``` - -**Usage Examples:** - -```python -from rich.color import Color -from rich import inspect -from rich.console import Console - -console = Console() - -# Color operations -red = Color.parse("red") -blue = Color.parse("#0000FF") -purple = red.blend(blue, 0.5) - -# Object inspection -inspect([1, 2, 3, 4, 5]) -inspect(console, methods=True) -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-rich/tile.json b/.tessl/tiles/tessl/pypi-rich/tile.json deleted file mode 100644 index 963f8beee..000000000 --- a/.tessl/tiles/tessl/pypi-rich/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-rich", - "version": "14.1.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/rich@14.1.0", - "summary": "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -} \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-ty/docs/index.md b/.tessl/tiles/tessl/pypi-ty/docs/index.md deleted file mode 100644 index c9b757e10..000000000 --- a/.tessl/tiles/tessl/pypi-ty/docs/index.md +++ /dev/null @@ -1,413 +0,0 @@ -# ty - -An extremely fast Python type checker and language server written in Rust. ty provides comprehensive type checking for Python codebases with advanced error reporting, configuration options, and Language Server Protocol support for seamless editor integration. - -## Package Information - -- **Package Name**: ty -- **Package Type**: pypi -- **Language**: Python (with Rust backend) -- **Installation**: `pip install ty` or `uv tool install ty` or `pipx install ty` -- **Version**: 0.0.1-alpha.20 -- **License**: MIT -- **Repository**: https://github.com/astral-sh/ty -- **Documentation**: https://docs.astral.sh/ty/ - -## Core Imports - -The ty package can be imported as a Python module but provides minimal programmatic API: - -```python -import ty -``` - -However, ty is primarily used as a command-line tool: - -```bash -ty check -``` - -## Basic Usage - -```bash -# Basic type checking -ty check - -# Check specific files -ty check src/main.py tests/ - -# Watch mode with auto-recheck -ty check --watch - -# Use specific Python version -ty check --python-version 3.11 - -# Start language server -ty server -``` - -For project integration: - -```bash -# Add to project as dev dependency -uv add --dev ty - -# Run with uv -uv run ty check -``` - -## Architecture - -ty consists of several key components: - -- **Type Checker Core**: Rust-based high-performance type analysis engine -- **CLI Interface**: Command-line interface for type checking operations -- **Language Server**: LSP implementation for real-time editor integration -- **Configuration System**: Flexible TOML-based configuration for projects -- **Module Resolution**: Sophisticated Python package and module discovery -- **Binary Wrapper**: Python wrapper that locates and executes the Rust binary - -The architecture prioritizes performance through Rust implementation while maintaining compatibility with Python tooling and ecosystems. - -## Capabilities - -### Command Line Interface - -Core command-line functionality for type checking, language server operation, and tool management. - -```bash { .api } -# Main command -ty - -# Type checking -ty check [OPTIONS] [PATHS]... - -# Language server -ty server - -# Version information -ty version - -# Shell completion -ty generate-shell-completion - -# Help -ty help [COMMAND] -``` - -**Arguments:** -- `PATHS`: List of files or directories to check (optional, defaults to project root) -- `SHELL`: Shell type for completion generation (bash, zsh, fish, powershell, elvish) -- `COMMAND`: Subcommand to get help for (optional) - -**Global Options for `ty check`:** - -File and path options: -- `--project `: Run within specific project directory -- `--exclude `: Glob patterns to exclude from checking - -Python environment options: -- `--python `: Path to Python environment or interpreter. ty uses this to resolve type information and third-party dependencies. If not specified, ty attempts to infer from VIRTUAL_ENV/CONDA_PREFIX or discover .venv directory. If interpreter path is provided (e.g., .venv/bin/python3), ty attempts to find environment two directories up. Does not invoke interpreter, so won't resolve dynamic executables like shims. -- `--python-version `, `--target-version `: Target Python version (3.7-3.13). If not specified, ty tries: 1) project.requires-python in pyproject.toml, 2) infer from Python environment, 3) fallback to 3.13 -- `--python-platform `, `--platform `: Target platform (win32/darwin/linux/android/ios/all). Used to specialize sys.platform type. If 'all', no platform assumptions are made. Defaults to current system platform. -- `--extra-search-path `: Additional module resolution paths (can be repeated) -- `--typeshed `, `--custom-typeshed-dir `: Custom typeshed directory for standard library types - -Configuration options: -- `--config-file `: Path to ty.toml configuration file -- `--config `, `-c `: TOML key=value configuration override - -Rule control options: -- `--error `: Treat rule as error-level (can be repeated) -- `--warn `: Treat rule as warning-level (can be repeated) -- `--ignore `: Disable rule (can be repeated) - -Output options: -- `--output-format `: Message format (full/concise) -- `--color `: Color output control (auto/always/never) -- `--quiet/-q`: Quiet output (use -qq for silent) -- `--verbose/-v`: Verbose output (use -vv, -vvv for more verbose) -- `--help/-h`: Print help information - -Behavior options: -- `--watch/-W`: Watch mode for continuous checking -- `--error-on-warning`: Exit code 1 on warnings -- `--exit-zero`: Always exit with code 0 -- `--respect-ignore-files`: Respect file exclusions via .gitignore and other standard ignore files (use --no-respect-gitignore to disable) - -### Python Module API - -Limited programmatic access through Python module wrapper. - -```python { .api } -def find_ty_bin() -> str: - """ - Return the ty binary path. - - Searches various installation locations including: - - System scripts directory - - User-specific installation paths - - Virtual environment paths - - pip build environments - - Adjacent bin directories - - Returns: - str: Absolute path to ty binary executable - - Raises: - FileNotFoundError: If ty binary cannot be located - """ -``` - -**Usage Example:** -```python -from ty import find_ty_bin -import subprocess - -# Get ty binary path -ty_path = find_ty_bin() - -# Run type checking programmatically -result = subprocess.run([ty_path, "check", "src/"], capture_output=True, text=True) -print(result.stdout) -``` - -### Configuration System - -Comprehensive TOML-based configuration supporting project-level and user-level settings. - -```toml { .api } -# pyproject.toml format -[tool.ty.rules] -rule_name = "ignore" | "warn" | "error" - -[tool.ty.environment] -extra-paths = ["./shared/my-search-path"] -python = "./.venv" -python-platform = "win32" | "darwin" | "android" | "ios" | "linux" | "all" -python-version = "3.7" | "3.8" | "3.9" | "3.10" | "3.11" | "3.12" | "3.13" -root = ["./src", "./lib"] -typeshed = "/path/to/custom/typeshed" - -[tool.ty.src] -include = ["src", "tests"] -exclude = ["generated", "*.proto", "tests/fixtures/**"] # Additional excludes beyond defaults -respect-ignore-files = true - -[tool.ty.terminal] -output-format = "full" | "concise" -error-on-warning = false - -[[tool.ty.overrides]] -include = ["tests/**"] -exclude = ["tests/fixtures/**"] - -[tool.ty.overrides.rules] -rule_name = "ignore" | "warn" | "error" -``` - -**Configuration File Locations:** -- Project: `pyproject.toml` (under `[tool.ty]`) or `ty.toml` -- User: `~/.config/ty/ty.toml` or `$XDG_CONFIG_HOME/ty/ty.toml` (Linux/macOS) -- User: `%APPDATA%\ty\ty.toml` (Windows) - -**Precedence:** Command line > project config > user config - -### Language Server Protocol - -Language server implementation providing real-time type checking and editor integration. - -```bash { .api } -# Start language server -ty server -``` - -**LSP Features:** -- Real-time type checking as you type -- Diagnostic reporting with error/warning details -- Hover information for type details -- Go-to-definition support -- Symbol search and workspace analysis -- Configuration synchronization - -**Editor Integration:** - -VS Code: -```json -{ - "ty.enable": true, - "ty.path": "/path/to/ty", - "ty.settings": { - "python-version": "3.11" - } -} -``` - -Neovim (nvim-lspconfig): -```lua -require('lspconfig').ty.setup({ - settings = { - ty = { - -- ty language server settings - } - } -}) -``` - -Neovim 0.11+ (vim.lsp.config): -```lua -vim.lsp.config('ty', { - settings = { - ty = { - -- ty language server settings - } - } -}) -vim.lsp.enable('ty') -``` - -### Environment Variables - -Environment variables that control ty's behavior and configuration. - -```bash { .api } -# Configuration file path -export TY_CONFIG_FILE="/path/to/ty.toml" - -# Python environment detection -export VIRTUAL_ENV="/path/to/venv" -export CONDA_PREFIX="/path/to/conda/env" -``` - -**Environment Variables:** -- `TY_CONFIG_FILE`: Override configuration file location -- `VIRTUAL_ENV`: Active virtual environment path (auto-detected) -- `CONDA_PREFIX`: Conda environment path (auto-detected) - -### Exit Codes - -ty uses standard exit codes to indicate the result of operations. - -```bash { .api } -# Exit codes -0 # Success (no errors, or only warnings without --error-on-warning) -1 # Type errors found, or warnings with --error-on-warning -2 # Configuration or runtime errors -``` - -## Module Discovery - -ty implements sophisticated module resolution for Python projects: - -### First-party Modules -- Searches project root and `src/` directories by default -- Configurable via `environment.root` setting -- Supports src-layout, flat-layout, and custom project structures -- Automatically includes `tests/` if it's not a package - -### Third-party Modules -- Resolves from configured Python environment -- Searches `site-packages` directories -- Supports virtual environments and conda environments -- Uses `--python` or auto-discovery via `VIRTUAL_ENV`/`CONDA_PREFIX` - -### Standard Library -- Uses built-in typeshed stubs (bundled as zip in binary) -- Version-specific based on target Python version -- Customizable via `--typeshed` option - -## Rule System - -ty provides a comprehensive rule system for type checking configuration: - -### Rule Categories -- Type compatibility and assignment checking -- Import resolution and missing import detection -- Attribute access validation -- Function call signature validation -- Variable binding and scope analysis - -### Rule Configuration -```toml -[tool.ty.rules] -# Examples of common rules -possibly-unresolved-reference = "warn" -division-by-zero = "ignore" -index-out-of-bounds = "error" -redundant-cast = "ignore" -unused-ignore-comment = "warn" -``` - -### Rule Overrides -```toml -# File-specific rule overrides -[[tool.ty.overrides]] -include = ["tests/**", "**/test_*.py"] - -[tool.ty.overrides.rules] -possibly-unresolved-reference = "ignore" # Relax for tests -``` - -## Performance Features - -ty is optimized for speed and efficiency: - -- **Rust Implementation**: Core type checker written in Rust for maximum performance -- **Incremental Checking**: Only re-analyzes changed files and dependencies -- **Watch Mode**: Continuous checking with file system monitoring -- **Fast Startup**: Minimal initialization overhead -- **Memory Efficient**: Optimized memory usage for large codebases -- **Parallel Analysis**: Multi-threaded processing where possible - -## Integration Patterns - -### CI/CD Integration -```yaml -# GitHub Actions example -- name: Type check with ty - run: | - uv tool install ty - ty check --error-on-warning -``` - -### Pre-commit Hook -```yaml -# .pre-commit-config.yaml -repos: - - repo: https://github.com/astral-sh/ty - rev: v0.0.1-alpha.20 - hooks: - - id: ty -``` - -### Development Workflow -```bash -# Development commands -ty check --watch # Continuous checking -ty check --verbose # Detailed output -ty check src/ tests/ # Specific paths -ty check --python .venv # Specific environment -``` - -### Editor Integration -- **VS Code**: Official extension (astral-sh.ty) -- **Neovim**: Built-in LSP support -- **Vim**: LSP client configuration -- **Emacs**: lsp-mode integration -- **Any LSP-compatible editor**: Generic LSP server support - -## Types - -```python { .api } -# Configuration types (for reference) -RuleName = str # Rule identifier (e.g., "possibly-unresolved-reference") -Severity = "ignore" | "warn" | "error" -PythonVersion = "3.7" | "3.8" | "3.9" | "3.10" | "3.11" | "3.12" | "3.13" -Platform = "win32" | "darwin" | "android" | "ios" | "linux" | "all" -OutputFormat = "full" | "concise" -ColorOption = "auto" | "always" | "never" - -# Path specifications -Path = str # File or directory path -GlobPattern = str # Gitignore-style pattern -``` \ No newline at end of file diff --git a/.tessl/tiles/tessl/pypi-ty/tile.json b/.tessl/tiles/tessl/pypi-ty/tile.json deleted file mode 100644 index c9b0da92b..000000000 --- a/.tessl/tiles/tessl/pypi-ty/tile.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "tessl/pypi-ty", - "version": "0.0.0", - "docs": "docs/index.md", - "describes": "pkg:pypi/ty@0.0.1-alpha.20", - "summary": "An extremely fast Python type checker and language server, written in Rust." -} \ No newline at end of file From 91e6e7dd4a6b621077bba504dadcfc8795b556ff Mon Sep 17 00:00:00 2001 From: Alex Zhang Date: Mon, 2 Feb 2026 13:32:12 -0500 Subject: [PATCH 4/4] add back default uv lock --- .gitignore | 2 +- uv.lock | 2487 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2488 insertions(+), 1 deletion(-) create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore index 3679d8414..7a8c14aa6 100644 --- a/.gitignore +++ b/.gitignore @@ -106,7 +106,7 @@ ipython_config.py # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. -uv.lock +# uv.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..aaf06922a --- /dev/null +++ b/uv.lock @@ -0,0 +1,2487 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version < '3.14'", +] + +[[package]] +name = "aiofiles" +version = "24.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247, upload-time = "2024-06-24T11:02:03.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896, upload-time = "2024-06-24T11:02:01.529Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, + { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, + { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, + { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, + { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, + { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, + { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, + { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, + { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, + { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, + { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, + { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, + { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, + { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, + { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, + { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, + { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, + { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, + { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, + { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, + { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, + { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, + { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, + { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, +] + +[[package]] +name = "aiohttp-retry" +version = "2.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/61/ebda4d8e3d8cfa1fd3db0fb428db2dd7461d5742cea35178277ad180b033/aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1", size = 13608, upload-time = "2024-11-06T10:44:54.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/99/84ba7273339d0f3dfa57901b846489d2e5c2cd731470167757f1935fffbd/aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54", size = 9981, upload-time = "2024-11-06T10:44:52.917Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anthropic" +version = "0.77.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "docstring-parser" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/85/6cb5da3cf91de2eeea89726316e8c5c8c31e2d61ee7cb1233d7e95512c31/anthropic-0.77.0.tar.gz", hash = "sha256:ce36efeb80cb1e25430a88440dc0f9aa5c87f10d080ab70a1bdfd5c2c5fbedb4", size = 504575, upload-time = "2026-01-29T18:20:41.507Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/27/9df785d3f94df9ac72f43ee9e14b8120b37d992b18f4952774ed46145022/anthropic-0.77.0-py3-none-any.whl", hash = "sha256:65cc83a3c82ce622d5c677d0d7706c77d29dc83958c6b10286e12fda6ffb2651", size = 397867, upload-time = "2026-01-29T18:20:39.481Z" }, +] + +[[package]] +name = "anyio" +version = "4.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "cached-property" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/4b/3d870836119dbe9a5e3c9a61af8cc1a8b69d75aea564572e385882d5aefb/cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641", size = 10574, upload-time = "2024-10-25T15:43:55.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/0e/7d8225aab3bc1a0f5811f8e1b557aa034ac04bdf641925b30d3caf586b28/cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb", size = 7428, upload-time = "2024-10-25T15:43:54.711Z" }, +] + +[[package]] +name = "cbor2" +version = "5.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/8e/8b4fdde28e42ffcd741a37f4ffa9fb59cd4fe01625b544dfcfd9ccb54f01/cbor2-5.8.0.tar.gz", hash = "sha256:b19c35fcae9688ac01ef75bad5db27300c2537eb4ee00ed07e05d8456a0d4931", size = 107825, upload-time = "2025-12-30T18:44:22.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/4b/623435ef9b98e86b6956a41863d39ff4fe4d67983948b5834f55499681dd/cbor2-5.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:18ac191640093e6c7fbcb174c006ffec4106c3d8ab788e70272c1c4d933cbe11", size = 69875, upload-time = "2025-12-30T18:43:35.888Z" }, + { url = "https://files.pythonhosted.org/packages/58/17/f664201080b2a7d0f57c16c8e9e5922013b92f202e294863ec7e75b7ff7f/cbor2-5.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fddee9103a17d7bed5753f0c7fc6663faa506eb953e50d8287804eccf7b048e6", size = 268316, upload-time = "2025-12-30T18:43:37.161Z" }, + { url = "https://files.pythonhosted.org/packages/d0/e1/072745b4ff01afe9df2cd627f8fc51a1acedb5d3d1253765625d2929db91/cbor2-5.8.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8d2ea26fad620aba5e88d7541be8b10c5034a55db9a23809b7cb49f36803f05b", size = 258874, upload-time = "2025-12-30T18:43:38.878Z" }, + { url = "https://files.pythonhosted.org/packages/a7/10/61c262b886d22b62c56e8aac6d10fa06d0953c997879ab882a31a624952b/cbor2-5.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:de68b4b310b072b082d317adc4c5e6910173a6d9455412e6183d72c778d1f54c", size = 261971, upload-time = "2025-12-30T18:43:40.401Z" }, + { url = "https://files.pythonhosted.org/packages/7e/42/b7862f5e64364b10ad120ea53e87ec7e891fb268cb99c572348e647cf7e9/cbor2-5.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:418d2cf0e03e90160fa1474c05a40fe228bbb4a92d1628bdbbd13a48527cb34d", size = 254151, upload-time = "2025-12-30T18:43:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/16/6a/8d3636cf75466c18615e7cfac0d345ee3c030f6c79535faed0c2c02b1839/cbor2-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:453200ffa1c285ea46ab5745736a015526d41f22da09cb45594624581d959770", size = 69169, upload-time = "2025-12-30T18:43:43.424Z" }, + { url = "https://files.pythonhosted.org/packages/9b/88/79b205bf869558b39a11de70750cb13679b27ba5654a43bed3f2aee7d1b4/cbor2-5.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:f6615412fca973a8b472b3efc4dab01df71cc13f15d8b2c0a1cffac44500f12d", size = 64955, upload-time = "2025-12-30T18:43:44.7Z" }, + { url = "https://files.pythonhosted.org/packages/2f/4f/3a16e3e8fd7e5fd86751a4f1aad218a8d19a96e75ec3989c3e95a8fe1d8f/cbor2-5.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b3f91fa699a5ce22470e973601c62dd9d55dc3ca20ee446516ac075fcab27c9", size = 70270, upload-time = "2025-12-30T18:43:46.005Z" }, + { url = "https://files.pythonhosted.org/packages/38/81/0d0cf0796fe8081492a61c45278f03def21a929535a492dd97c8438f5dbe/cbor2-5.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:518c118a5e00001854adb51f3164e647aa99b6a9877d2a733a28cb5c0a4d6857", size = 286242, upload-time = "2025-12-30T18:43:47.026Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/fdab6c10190cfb8d639e01f2b168f2406fc847a2a6bc00e7de78c3381d0a/cbor2-5.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cff2a1999e49cd51c23d1b6786a012127fd8f722c5946e82bd7ab3eb307443f3", size = 285412, upload-time = "2025-12-30T18:43:48.563Z" }, + { url = "https://files.pythonhosted.org/packages/31/59/746a8e630996217a3afd523f583fcf7e3d16640d63f9a03f0f4e4f74b5b1/cbor2-5.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c4492160212374973cdc14e46f0565f2462721ef922b40f7ea11e7d613dfb2a", size = 278041, upload-time = "2025-12-30T18:43:49.92Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a3/f3bbeb6dedd45c6e0cddd627ea790dea295eaf82c83f0e2159b733365ebd/cbor2-5.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:546c7c7c4c6bcdc54a59242e0e82cea8f332b17b4465ae628718fef1fce401ca", size = 278185, upload-time = "2025-12-30T18:43:51.192Z" }, + { url = "https://files.pythonhosted.org/packages/67/e5/9013d6b857ceb6cdb2851ffb5a887f53f2bab934a528c9d6fa73d9989d84/cbor2-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:074f0fa7535dd7fdee247c2c99f679d94f3aa058ccb1ccf4126cc72d6d89cbae", size = 69817, upload-time = "2025-12-30T18:43:52.352Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ab/7aa94ba3d44ecbc3a97bdb2fb6a8298063fe2e0b611e539a6fe41e36da20/cbor2-5.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:f95fed480b2a0d843f294d2a1ef4cc0f6a83c7922927f9f558e1f5a8dc54b7ca", size = 64923, upload-time = "2025-12-30T18:43:53.719Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0d/5a3f20bafaefeb2c1903d961416f051c0950f0d09e7297a3aa6941596b29/cbor2-5.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d8d104480845e2f28c6165b4c961bbe58d08cb5638f368375cfcae051c28015", size = 70332, upload-time = "2025-12-30T18:43:54.694Z" }, + { url = "https://files.pythonhosted.org/packages/57/66/177a3f089e69db69c987453ab4934086408c3338551e4984734597be9f80/cbor2-5.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43efee947e5ab67d406d6e0dc61b5dee9d2f5e89ae176f90677a3741a20ca2e7", size = 285985, upload-time = "2025-12-30T18:43:55.733Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9e17b8e4ed80a2ce97e2dfa5915c169dbb31599409ddb830f514b57f96cc/cbor2-5.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be7ae582f50be539e09c134966d0fd63723fc4789b8dff1f6c2e3f24ae3eaf32", size = 285173, upload-time = "2025-12-30T18:43:57.321Z" }, + { url = "https://files.pythonhosted.org/packages/cc/33/9f92e107d78f88ac22723ac15d0259d220ba98c1d855e51796317f4c4114/cbor2-5.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50f5c709561a71ea7970b4cd2bf9eda4eccacc0aac212577080fdfe64183e7f5", size = 278395, upload-time = "2025-12-30T18:43:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3f/46b80050a4a35ce5cf7903693864a9fdea7213567dc8faa6e25cb375c182/cbor2-5.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6790ecc73aa93e76d2d9076fc42bf91a9e69f2295e5fa702e776dbe986465bd", size = 278330, upload-time = "2025-12-30T18:43:59.656Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/d41f8c04c783a4d204e364be2d38043d4f732a3bed6f4c732e321cf34c7b/cbor2-5.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:c114af8099fa65a19a514db87ce7a06e942d8fea2730afd49be39f8e16e7f5e0", size = 69841, upload-time = "2025-12-30T18:44:01.159Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8c/0397a82f6e67665009951453c83058e4c77ba54b9a9017ede56d6870306c/cbor2-5.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:ab3ba00494ad8669a459b12a558448d309c271fa4f89b116ad496ee35db38fea", size = 64982, upload-time = "2025-12-30T18:44:02.138Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0c/0654233d7543ac8a50f4785f172430ddc97538ba418eb305d6e529d1a120/cbor2-5.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ad72381477133046ce217617d839ea4e9454f8b77d9a6351b229e214102daeb7", size = 70710, upload-time = "2025-12-30T18:44:03.209Z" }, + { url = "https://files.pythonhosted.org/packages/84/62/4671d24e557d7f5a74a01b422c538925140c0495e57decde7e566f91d029/cbor2-5.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6da25190fad3434ce99876b11d4ca6b8828df6ca232cf7344cd14ae1166fb718", size = 285005, upload-time = "2025-12-30T18:44:05.109Z" }, + { url = "https://files.pythonhosted.org/packages/87/85/0c67d763a08e848c9a80d7e4723ba497cce676f41bc7ca1828ae90a0a872/cbor2-5.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c13919e3a24c5a6d286551fa288848a4cedc3e507c58a722ccd134e461217d99", size = 282435, upload-time = "2025-12-30T18:44:06.465Z" }, + { url = "https://files.pythonhosted.org/packages/b2/01/0650972b4dbfbebcfbe37cbba7fc3cd9019a8da6397ab3446e07175e342b/cbor2-5.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f8c40d32e5972047a777f9bf730870828f3cf1c43b3eb96fd0429c57a1d3b9e6", size = 277493, upload-time = "2025-12-30T18:44:07.609Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6c/7704a4f32adc7f10f3b41ec067f500a4458f7606397af5e4cf2d368fd288/cbor2-5.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7627894bc0b3d5d0807f31e3107e11b996205470c4429dc2bb4ef8bfe7f64e1e", size = 276085, upload-time = "2025-12-30T18:44:09.021Z" }, + { url = "https://files.pythonhosted.org/packages/88/6d/e43452347630efe8133f5304127539100d937c138c0996d27ec63963ec2c/cbor2-5.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:b51c5e59becae746ca4de2bbaa8a2f5c64a68fec05cea62941b1a84a8335f7d1", size = 71657, upload-time = "2025-12-30T18:44:10.162Z" }, + { url = "https://files.pythonhosted.org/packages/8b/66/9a780ef34ab10a0437666232e885378cdd5f60197b1b5e61a62499e5a10a/cbor2-5.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:53b630f4db4b9f477ad84077283dd17ecf9894738aa17ef4938c369958e02a71", size = 67171, upload-time = "2025-12-30T18:44:11.619Z" }, + { url = "https://files.pythonhosted.org/packages/d6/4f/101071f880b4da05771128c0b89f41e334cff044dee05fb013c8f4be661c/cbor2-5.8.0-py3-none-any.whl", hash = "sha256:3727d80f539567b03a7aa11890e57798c67092c38df9e6c23abb059e0f65069c", size = 24374, upload-time = "2025-12-30T18:44:21.476Z" }, +] + +[[package]] +name = "certifi" +version = "2026.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, + { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[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, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/01/abca50583a8975bb6e1c59eff67ed8e48bb127c07dad5c28d9e96ccc09ec/coverage-7.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:060ebf6f2c51aff5ba38e1f43a2095e087389b1c69d559fde6049a4b0001320e", size = 218971, upload-time = "2026-01-25T12:57:36.953Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0e/b6489f344d99cd1e5b4d5e1be52dfd3f8a3dc5112aa6c33948da8cabad4e/coverage-7.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1ea8ca9db5e7469cd364552985e15911548ea5b69c48a17291f0cac70484b2e", size = 219473, upload-time = "2026-01-25T12:57:38.934Z" }, + { url = "https://files.pythonhosted.org/packages/17/11/db2f414915a8e4ec53f60b17956c27f21fb68fcf20f8a455ce7c2ccec638/coverage-7.13.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b780090d15fd58f07cf2011943e25a5f0c1c894384b13a216b6c86c8a8a7c508", size = 249896, upload-time = "2026-01-25T12:57:40.365Z" }, + { url = "https://files.pythonhosted.org/packages/80/06/0823fe93913663c017e508e8810c998c8ebd3ec2a5a85d2c3754297bdede/coverage-7.13.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:88a800258d83acb803c38175b4495d293656d5fac48659c953c18e5f539a274b", size = 251810, upload-time = "2026-01-25T12:57:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/61/dc/b151c3cc41b28cdf7f0166c5fa1271cbc305a8ec0124cce4b04f74791a18/coverage-7.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6326e18e9a553e674d948536a04a80d850a5eeefe2aae2e6d7cf05d54046c01b", size = 253920, upload-time = "2026-01-25T12:57:44.026Z" }, + { url = "https://files.pythonhosted.org/packages/2d/35/e83de0556e54a4729a2b94ea816f74ce08732e81945024adee46851c2264/coverage-7.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:59562de3f797979e1ff07c587e2ac36ba60ca59d16c211eceaa579c266c5022f", size = 250025, upload-time = "2026-01-25T12:57:45.624Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/af2eb9c3926ce3ea0d58a0d2516fcbdacf7a9fc9559fe63076beaf3f2596/coverage-7.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:27ba1ed6f66b0e2d61bfa78874dffd4f8c3a12f8e2b5410e515ab345ba7bc9c3", size = 251612, upload-time = "2026-01-25T12:57:47.713Z" }, + { url = "https://files.pythonhosted.org/packages/26/62/5be2e25f3d6c711d23b71296f8b44c978d4c8b4e5b26871abfc164297502/coverage-7.13.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8be48da4d47cc68754ce643ea50b3234557cbefe47c2f120495e7bd0a2756f2b", size = 249670, upload-time = "2026-01-25T12:57:49.378Z" }, + { url = "https://files.pythonhosted.org/packages/b3/51/400d1b09a8344199f9b6a6fc1868005d766b7ea95e7882e494fa862ca69c/coverage-7.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2a47a4223d3361b91176aedd9d4e05844ca67d7188456227b6bf5e436630c9a1", size = 249395, upload-time = "2026-01-25T12:57:50.86Z" }, + { url = "https://files.pythonhosted.org/packages/e0/36/f02234bc6e5230e2f0a63fd125d0a2093c73ef20fdf681c7af62a140e4e7/coverage-7.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c6f141b468740197d6bd38f2b26ade124363228cc3f9858bd9924ab059e00059", size = 250298, upload-time = "2026-01-25T12:57:52.287Z" }, + { url = "https://files.pythonhosted.org/packages/b0/06/713110d3dd3151b93611c9cbfc65c15b4156b44f927fced49ac0b20b32a4/coverage-7.13.2-cp311-cp311-win32.whl", hash = "sha256:89567798404af067604246e01a49ef907d112edf2b75ef814b1364d5ce267031", size = 221485, upload-time = "2026-01-25T12:57:53.876Z" }, + { url = "https://files.pythonhosted.org/packages/16/0c/3ae6255fa1ebcb7dec19c9a59e85ef5f34566d1265c70af5b2fc981da834/coverage-7.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:21dd57941804ae2ac7e921771a5e21bbf9aabec317a041d164853ad0a96ce31e", size = 222421, upload-time = "2026-01-25T12:57:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/b5/37/fabc3179af4d61d89ea47bd04333fec735cd5e8b59baad44fed9fc4170d7/coverage-7.13.2-cp311-cp311-win_arm64.whl", hash = "sha256:10758e0586c134a0bafa28f2d37dd2cdb5e4a90de25c0fc0c77dabbad46eca28", size = 221088, upload-time = "2026-01-25T12:57:57.41Z" }, + { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" }, + { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" }, + { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" }, + { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" }, + { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" }, + { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" }, + { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" }, + { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" }, + { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" }, + { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" }, + { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" }, + { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" }, + { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" }, + { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" }, + { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" }, + { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" }, + { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" }, + { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" }, + { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" }, + { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" }, + { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" }, + { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" }, + { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" }, + { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" }, + { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" }, + { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" }, + { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" }, + { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" }, + { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" }, + { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" }, + { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" }, + { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" }, + { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" }, + { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" }, + { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" }, + { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" }, + { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" }, + { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "cryptography" +version = "46.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, + { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, + { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, + { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, + { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, + { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, + { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, + { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, + { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, + { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, + { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, + { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, + { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, + { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, + { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, + { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, + { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, + { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, + { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, + { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, + { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, + { url = "https://files.pythonhosted.org/packages/59/e0/f9c6c53e1f2a1c2507f00f2faba00f01d2f334b35b0fbfe5286715da2184/cryptography-46.0.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:766330cce7416c92b5e90c3bb71b1b79521760cdcfc3a6a1a182d4c9fab23d2b", size = 3476316, upload-time = "2026-01-28T00:24:24.144Z" }, + { url = "https://files.pythonhosted.org/packages/27/7a/f8d2d13227a9a1a9fe9c7442b057efecffa41f1e3c51d8622f26b9edbe8f/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c236a44acfb610e70f6b3e1c3ca20ff24459659231ef2f8c48e879e2d32b73da", size = 4216693, upload-time = "2026-01-28T00:24:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/c5/de/3787054e8f7972658370198753835d9d680f6cd4a39df9f877b57f0dd69c/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8a15fb869670efa8f83cbffbc8753c1abf236883225aed74cd179b720ac9ec80", size = 4382765, upload-time = "2026-01-28T00:24:27.577Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5f/60e0afb019973ba6a0b322e86b3d61edf487a4f5597618a430a2a15f2d22/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:fdc3daab53b212472f1524d070735b2f0c214239df131903bae1d598016fa822", size = 4216066, upload-time = "2026-01-28T00:24:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/81/8e/bf4a0de294f147fee66f879d9bae6f8e8d61515558e3d12785dd90eca0be/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:44cc0675b27cadb71bdbb96099cca1fa051cd11d2ade09e5cd3a2edb929ed947", size = 4382025, upload-time = "2026-01-28T00:24:30.681Z" }, + { url = "https://files.pythonhosted.org/packages/79/f4/9ceb90cfd6a3847069b0b0b353fd3075dc69b49defc70182d8af0c4ca390/cryptography-46.0.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be8c01a7d5a55f9a47d1888162b76c8f49d62b234d88f0ff91a9fbebe32ffbc3", size = 3406043, upload-time = "2026-01-28T00:24:32.236Z" }, +] + +[[package]] +name = "daytona" +version = "0.138.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "daytona-api-client" }, + { name = "daytona-api-client-async" }, + { name = "daytona-toolbox-api-client" }, + { name = "daytona-toolbox-api-client-async" }, + { name = "deprecated" }, + { name = "environs" }, + { name = "httpx" }, + { name = "multipart" }, + { name = "obstore" }, + { name = "pydantic" }, + { name = "toml" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/d1/cbc662816f1e4dc75a8e93d8a903f6767031f0a229a1f848663a211fd1f9/daytona-0.138.0.tar.gz", hash = "sha256:ca7a0b2817a911ec523604e4ecda6d26336d8d08b0a5418ebc6d06e6e11238b5", size = 116073, upload-time = "2026-01-30T15:22:04.023Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/12/dc0ba5d82608b4e61048347d5a71a98c42d46ad724bd2c5b6cdf9a0f22f3/daytona-0.138.0-py3-none-any.whl", hash = "sha256:9cdb7960bb9fe356118e61050bdfbbba2446549bca5c56cc5da0be0b031bef2a", size = 144233, upload-time = "2026-01-30T15:22:02.476Z" }, +] + +[[package]] +name = "daytona-api-client" +version = "0.138.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/25/59174dc00d2e2866500b1838c82bfc4ed6823ceacffbf8274579be208862/daytona_api_client-0.138.0.tar.gz", hash = "sha256:cb13f6d4d1b8e47c872a98b3730a7a14b3e45597b54b30a22dbdb7f2539debd0", size = 134843, upload-time = "2026-01-30T15:20:44.993Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/70/fa596245d6f8a4085108b5f7ecdcf64479549792128b29113b4390e7e9be/daytona_api_client-0.138.0-py3-none-any.whl", hash = "sha256:7b90f9649488cc833254aef7d99423705d19730655934f87074b5d0d0bf553aa", size = 375462, upload-time = "2026-01-30T15:20:43.48Z" }, +] + +[[package]] +name = "daytona-api-client-async" +version = "0.138.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aiohttp-retry" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/67/de2a349f913aa711efc6a130d91efccad4eddfc2ef08bb9ceb69e0b7c7f3/daytona_api_client_async-0.138.0.tar.gz", hash = "sha256:6afbcfe6d39b85084e9d0e6bb46cad04b63eea350784bbce1423f1ab21641eb1", size = 134993, upload-time = "2026-01-30T15:21:24.041Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/cb/039f1a63e7fc48c11cd83c0213bc12db3f4644aaf9e4121d1a81c3f1b8c3/daytona_api_client_async-0.138.0-py3-none-any.whl", hash = "sha256:fee31ee02e59c44f5f64a2a23610ecb4a5f195542555dac30c0142385ffeb273", size = 378303, upload-time = "2026-01-30T15:21:22.397Z" }, +] + +[[package]] +name = "daytona-toolbox-api-client" +version = "0.138.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/b9/a10ddb86dbc2a74a489c092a04838cc86a3a977a3d81df9c6c26bbae42d7/daytona_toolbox_api_client-0.138.0.tar.gz", hash = "sha256:e0f1659b9aabdfc17085695ee5bafb9c84d9552ad56d4527261854e95974b4d0", size = 62294, upload-time = "2026-01-30T15:20:57.063Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/4c/1ef92158dc940ecd2340ce9d4262562f6294e593703b7ea88551af4ff9c4/daytona_toolbox_api_client-0.138.0-py3-none-any.whl", hash = "sha256:b83b126f3f475549e62e9bfe06cd45fee13dea13fb92fba1c4cc4162e1fb299f", size = 163762, upload-time = "2026-01-30T15:20:55.586Z" }, +] + +[[package]] +name = "daytona-toolbox-api-client-async" +version = "0.138.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "aiohttp-retry" }, + { name = "pydantic" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c3/6a/96e9f065a753cc973727739ac58d7c4e313f67f4e6d4147b836e47a2a844/daytona_toolbox_api_client_async-0.138.0.tar.gz", hash = "sha256:f6ad6c26aa17ae2b7a1d8a382cd0d97d0017b5f1e9b35590628f3b0fb3fdb916", size = 59277, upload-time = "2026-01-30T15:21:06.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/4d/336abe24b85820a922ca5f07438bde183ced2705fc671dfe64665d1d4405/daytona_toolbox_api_client_async-0.138.0-py3-none-any.whl", hash = "sha256:bec5bfe1cd7a75ce38edd7740c5b743b203883d06957fc75119ac081b35e86a9", size = 165028, upload-time = "2026-01-30T15:21:05.287Z" }, +] + +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + +[[package]] +name = "environs" +version = "14.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/75/06801d5beeb398ed3903167af9376bb81c4ac41c44a53d45193065ebb1a8/environs-14.5.0.tar.gz", hash = "sha256:f7b8f6fcf3301bc674bc9c03e39b5986d116126ffb96764efd34c339ed9464ee", size = 35426, upload-time = "2025-11-02T21:30:36.78Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/f3/6961beb9a1e77d01dee1dd48f00fb3064429c8abcfa26aa863eb7cb2b6dd/environs-14.5.0-py3-none-any.whl", hash = "sha256:1abd3e3a5721fb09797438d6c902bc2f35d4580dfaffe68b8ee588b67b504e13", size = 17202, upload-time = "2025-11-02T21:30:35.186Z" }, +] + +[[package]] +name = "filelock" +version = "3.20.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "google-auth" +version = "2.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/41/242044323fbd746615884b1c16639749e73665b718209946ebad7ba8a813/google_auth-2.48.0.tar.gz", hash = "sha256:4f7e706b0cd3208a3d940a19a822c37a476ddba5450156c3e6624a71f7c841ce", size = 326522, upload-time = "2026-01-26T19:22:47.157Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/1d/d6466de3a5249d35e832a52834115ca9d1d0de6abc22065f049707516d47/google_auth-2.48.0-py3-none-any.whl", hash = "sha256:2e2a537873d449434252a9632c28bfc268b0adb1e53f9fb62afc5333a975903f", size = 236499, upload-time = "2026-01-26T19:22:45.099Z" }, +] + +[package.optional-dependencies] +requests = [ + { name = "requests" }, +] + +[[package]] +name = "google-genai" +version = "1.61.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "google-auth", extra = ["requests"] }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "sniffio" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/38/421cd7e70952a536be87a0249409f87297d84f523754a25b08fe94b97e7f/google_genai-1.61.0.tar.gz", hash = "sha256:5773a4e8ad5b2ebcd54a633a67d8e9c4f413032fef07977ee47ffa34a6d3bbdf", size = 489672, upload-time = "2026-01-30T20:50:27.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/87/78dd70cb59f7acf3350f53c5144a7aa7bc39c6f425cd7dc1224b59fcdac3/google_genai-1.61.0-py3-none-any.whl", hash = "sha256:cb073ef8287581476c1c3f4d8e735426ee34478e500a56deef218fa93071e3ca", size = 721948, upload-time = "2026-01-30T20:50:25.551Z" }, +] + +[[package]] +name = "grpclib" +version = "0.4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h2" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/28/5a2c299ec82a876a252c5919aa895a6f1d1d35c96417c5ce4a4660dc3a80/grpclib-0.4.9.tar.gz", hash = "sha256:cc589c330fa81004c6400a52a566407574498cb5b055fa927013361e21466c46", size = 84798, upload-time = "2025-12-14T22:23:14.349Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/90/b0cbbd9efcc82816c58f31a34963071aa19fb792a212a5d9caf8e0fc3097/grpclib-0.4.9-py3-none-any.whl", hash = "sha256:7762ec1c8ed94dfad597475152dd35cbd11aecaaca2f243e29702435ca24cf0e", size = 77063, upload-time = "2025-12-14T22:23:13.224Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "h2" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[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, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, +] + +[[package]] +name = "identify" +version = "2.6.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360, upload-time = "2026-01-12T18:58:58.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jiter" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/29/499f8c9eaa8a16751b1c0e45e6f5f1761d180da873d417996cc7bddc8eef/jiter-0.13.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ea026e70a9a28ebbdddcbcf0f1323128a8db66898a06eaad3a4e62d2f554d096", size = 311157, upload-time = "2026-02-02T12:35:37.758Z" }, + { url = "https://files.pythonhosted.org/packages/50/f6/566364c777d2ab450b92100bea11333c64c38d32caf8dc378b48e5b20c46/jiter-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66aa3e663840152d18cc8ff1e4faad3dd181373491b9cfdc6004b92198d67911", size = 319729, upload-time = "2026-02-02T12:35:39.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/560f13ec5e4f116d8ad2658781646cca91b617ae3b8758d4a5076b278f70/jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3524798e70655ff19aec58c7d05adb1f074fecff62da857ea9be2b908b6d701", size = 354766, upload-time = "2026-02-02T12:35:40.662Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0d/061faffcfe94608cbc28a0d42a77a74222bdf5055ccdbe5fd2292b94f510/jiter-0.13.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec7e287d7fbd02cb6e22f9a00dd9c9cd504c40a61f2c61e7e1f9690a82726b4c", size = 362587, upload-time = "2026-02-02T12:35:42.025Z" }, + { url = "https://files.pythonhosted.org/packages/92/c9/c66a7864982fd38a9773ec6e932e0398d1262677b8c60faecd02ffb67bf3/jiter-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47455245307e4debf2ce6c6e65a717550a0244231240dcf3b8f7d64e4c2f22f4", size = 487537, upload-time = "2026-02-02T12:35:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/6c/86/84eb4352cd3668f16d1a88929b5888a3fe0418ea8c1dfc2ad4e7bf6e069a/jiter-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee9da221dca6e0429c2704c1b3655fe7b025204a71d4d9b73390c759d776d165", size = 373717, upload-time = "2026-02-02T12:35:44.928Z" }, + { url = "https://files.pythonhosted.org/packages/6e/09/9fe4c159358176f82d4390407a03f506a8659ed13ca3ac93a843402acecf/jiter-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ab43126d5e05f3d53a36a8e11eb2f23304c6c1117844aaaf9a0aa5e40b5018", size = 362683, upload-time = "2026-02-02T12:35:46.636Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5e/85f3ab9caca0c1d0897937d378b4a515cae9e119730563572361ea0c48ae/jiter-0.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9da38b4fedde4fb528c740c2564628fbab737166a0e73d6d46cb4bb5463ff411", size = 392345, upload-time = "2026-02-02T12:35:48.088Z" }, + { url = "https://files.pythonhosted.org/packages/12/4c/05b8629ad546191939e6f0c2f17e29f542a398f4a52fb987bc70b6d1eb8b/jiter-0.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b34c519e17658ed88d5047999a93547f8889f3c1824120c26ad6be5f27b6cf5", size = 517775, upload-time = "2026-02-02T12:35:49.482Z" }, + { url = "https://files.pythonhosted.org/packages/4d/88/367ea2eb6bc582c7052e4baf5ddf57ebe5ab924a88e0e09830dfb585c02d/jiter-0.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2a6394e6af690d462310a86b53c47ad75ac8c21dc79f120714ea449979cb1d3", size = 551325, upload-time = "2026-02-02T12:35:51.104Z" }, + { url = "https://files.pythonhosted.org/packages/f3/12/fa377ffb94a2f28c41afaed093e0d70cfe512035d5ecb0cad0ae4792d35e/jiter-0.13.0-cp311-cp311-win32.whl", hash = "sha256:0f0c065695f616a27c920a56ad0d4fc46415ef8b806bf8fc1cacf25002bd24e1", size = 204709, upload-time = "2026-02-02T12:35:52.467Z" }, + { url = "https://files.pythonhosted.org/packages/cb/16/8e8203ce92f844dfcd3d9d6a5a7322c77077248dbb12da52d23193a839cd/jiter-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0733312953b909688ae3c2d58d043aa040f9f1a6a75693defed7bc2cc4bf2654", size = 204560, upload-time = "2026-02-02T12:35:53.925Z" }, + { url = "https://files.pythonhosted.org/packages/44/26/97cc40663deb17b9e13c3a5cf29251788c271b18ee4d262c8f94798b8336/jiter-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:5d9b34ad56761b3bf0fbe8f7e55468704107608512350962d3317ffd7a4382d5", size = 189608, upload-time = "2026-02-02T12:35:55.304Z" }, + { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, + { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, + { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, + { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, + { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, + { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, + { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, + { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, + { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, + { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, + { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, + { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, + { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, + { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, + { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, + { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, + { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, + { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, + { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, + { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, + { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, + { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, + { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, + { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, + { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, + { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, + { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, + { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, + { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, + { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, + { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, + { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/3c29819a27178d0e461a8571fb63c6ae38be6dc36b78b3ec2876bbd6a910/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c", size = 307016, upload-time = "2026-02-02T12:37:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/60993e4b07b1ac5ebe46da7aa99fdbb802eb986c38d26e3883ac0125c4e0/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2", size = 305024, upload-time = "2026-02-02T12:37:44.774Z" }, + { url = "https://files.pythonhosted.org/packages/77/fa/2227e590e9cf98803db2811f172b2d6460a21539ab73006f251c66f44b14/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434", size = 339337, upload-time = "2026-02-02T12:37:46.668Z" }, + { url = "https://files.pythonhosted.org/packages/2d/92/015173281f7eb96c0ef580c997da8ef50870d4f7f4c9e03c845a1d62ae04/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d", size = 346395, upload-time = "2026-02-02T12:37:48.09Z" }, + { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, + { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "marshmallow" +version = "4.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/61/9540e1b120e9eff74b24def6060b79347f2b4083dddbd5a435f6e8d74b14/marshmallow-4.2.1.tar.gz", hash = "sha256:4d1d66189c8d279ca73a6b0599d74117e5f8a3830b5cd766b75c2bb08e3464e7", size = 221309, upload-time = "2026-01-23T13:00:24.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/41/e8cfdebc3a475c397e074ceaa12e86dca1b23e8e10c2ab4d4875f9a01418/marshmallow-4.2.1-py3-none-any.whl", hash = "sha256:d82b1a83cfbb4667d050850fbed4e9d4435576cb95f5ff37894f375dce201768", size = 48451, upload-time = "2026-01-23T13:00:23.009Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "modal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "cbor2" }, + { name = "certifi" }, + { name = "click" }, + { name = "grpclib" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "synchronicity" }, + { name = "toml" }, + { name = "typer" }, + { name = "types-certifi" }, + { name = "types-toml" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/67/3913d3166326960357b43e30210c1b456ab37a117a3adc522b20e4a8cefb/modal-1.3.2.tar.gz", hash = "sha256:d80fd98ce7729405546252b7a6729baf3a9cb22b80d303779b0c0d48b9af3397", size = 661504, upload-time = "2026-01-30T19:18:51.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/2c/8b3d26ce904a79f238c46d66159edc2cbca3910cfd85ee8c35c6985e269a/modal-1.3.2-py3-none-any.whl", hash = "sha256:7aadace311e61d16223e197efe745cdcc887ff4b8cd28288f1c6059bd227d9c3", size = 758034, upload-time = "2026-01-30T19:18:50.432Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "multipart" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/c9/c6f5ab81bae667d4fe42a58df29f4c2db6ad8377cfd0e9baa729e4fa3ebb/multipart-1.3.0.tar.gz", hash = "sha256:a46bd6b0eb4c1ba865beb88ddd886012a3da709b6e7b86084fc37e99087e5cf1", size = 38816, upload-time = "2025-07-26T15:09:38.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/d6/d547a7004b81fa0b2aafa143b09196f6635e4105cd9d2c641fa8a4051c05/multipart-1.3.0-py3-none-any.whl", hash = "sha256:439bf4b00fd7cb2dbff08ae13f49f4f49798931ecd8d496372c63537fa19f304", size = 14938, upload-time = "2025-07-26T15:09:36.884Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "obstore" +version = "0.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/8c/9ec984edd0f3b72226adfaa19b1c61b15823b35b52f311ca4af36d009d15/obstore-0.8.2.tar.gz", hash = "sha256:a467bc4e97169e2ba749981b4fd0936015428d9b8f3fb83a5528536b1b6f377f", size = 168852, upload-time = "2025-09-16T15:34:55.786Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/c4/018f90701f1e5ea3fbd57f61463f42e1ef5218e548d3adcf12b6be021c34/obstore-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2edaa97687c191c5324bb939d72f6fe86a7aa8191c410f1648c14e8296d05c1c", size = 3622568, upload-time = "2025-09-16T15:33:14.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/62/72dd1e7d52fc554bb1fdb1a9499bda219cf3facea5865a1d97fdc00b3a1b/obstore-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c4fb7ef8108f08d14edc8bec9e9a6a2e5c4d14eddb8819f5d0da498aff6e8888", size = 3356109, upload-time = "2025-09-16T15:33:15.315Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ae/089fe5b9207091252fe5ce352551214f04560f85eb8f2cc4f716a6a1a57e/obstore-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fda8f658c0edf799ab1e264f9b12c7c184cd09a5272dc645d42e987810ff2772", size = 3454588, upload-time = "2025-09-16T15:33:16.421Z" }, + { url = "https://files.pythonhosted.org/packages/ea/10/1865ae2d1ba45e8ae85fb0c1aada2dc9533baf60c4dfe74dab905348d74a/obstore-0.8.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87fe2bc15ce4051ecb56abd484feca323c2416628beb62c1c7b6712114564d6e", size = 3688627, upload-time = "2025-09-16T15:33:17.604Z" }, + { url = "https://files.pythonhosted.org/packages/a6/09/5d7ba6d0aeac563ea5f5586401c677bace4f782af83522b1fdf15430e152/obstore-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2482aa2562ab6a4ca40250b26bea33f8375b59898a9b5615fd412cab81098123", size = 3959896, upload-time = "2025-09-16T15:33:18.789Z" }, + { url = "https://files.pythonhosted.org/packages/16/15/2b3eda59914761a9ff4d840e2daec5697fd29b293bd18d3dc11c593aed06/obstore-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4153b928f5d2e9c6cb645e83668a53e0b42253d1e8bcb4e16571fc0a1434599a", size = 3933162, upload-time = "2025-09-16T15:33:19.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/7a/5fc63b41526587067537fb1498c59a210884664c65ccf0d1f8f823b0875a/obstore-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbfa9c38620cc191be98c8b5558c62071e495dc6b1cc724f38293ee439aa9f92", size = 3769605, upload-time = "2025-09-16T15:33:21.389Z" }, + { url = "https://files.pythonhosted.org/packages/77/4e/2208ab6e1fc021bf8b7e117249a10ab75d0ed24e0f2de1a8d7cd67d885b5/obstore-0.8.2-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:0822836eae8d52499f10daef17f26855b4c123119c6eb984aa4f2d525ec2678d", size = 3534396, upload-time = "2025-09-16T15:33:22.574Z" }, + { url = "https://files.pythonhosted.org/packages/1d/8f/a0e2882edd6bd285c82b8a5851c4ecf386c93fe75b6e340d5d9d30e809fc/obstore-0.8.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8ef6435dfd586d83b4f778e7927a5d5b0d8b771e9ba914bc809a13d7805410e6", size = 3697777, upload-time = "2025-09-16T15:33:23.723Z" }, + { url = "https://files.pythonhosted.org/packages/94/78/ebf0c33bed5c9a8eed3b00eefafbcc0a687eeb1e05451c76fcf199d29ff8/obstore-0.8.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0f2cba91f4271ca95a932a51aa8dda1537160342b33f7836c75e1eb9d40621a2", size = 3681546, upload-time = "2025-09-16T15:33:24.935Z" }, + { url = "https://files.pythonhosted.org/packages/af/21/9bf4fb9e53fd5f01af580b6538de2eae857e31d24b0ebfc4d916c306a1e4/obstore-0.8.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:23c876d603af0627627808d19a58d43eb5d8bfd02eecd29460bc9a58030fed55", size = 3765336, upload-time = "2025-09-16T15:33:26.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3c/7f6895c23719482d231b2d6ed328e3223fdf99785f6850fba8d2fc5a86ee/obstore-0.8.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ff3c4b5d07629b70b9dee494cd6b94fff8465c3864752181a1cb81a77190fe42", size = 3941142, upload-time = "2025-09-16T15:33:27.275Z" }, + { url = "https://files.pythonhosted.org/packages/93/a4/56ccdb756161595680a28f4b0def2c04f7048ffacf128029be8394367b26/obstore-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:aadb2cb72de7227d07f4570f82729625ffc77522fadca5cf13c3a37fbe8c8de9", size = 3970172, upload-time = "2025-09-16T15:33:28.393Z" }, + { url = "https://files.pythonhosted.org/packages/2b/dc/60fefbb5736e69eab56657bca04ca64dc07fdeccb3814164a31b62ad066b/obstore-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bb70ce297a47392b1d9a3e310f18d59cd5ebbb9453428210fef02ed60e4d75d1", size = 3612955, upload-time = "2025-09-16T15:33:29.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8b/844e8f382e5a12b8a3796a05d76a03e12c7aedc13d6900419e39207d7868/obstore-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1619bf618428abf1f607e0b219b2e230a966dcf697b717deccfa0983dd91f646", size = 3346564, upload-time = "2025-09-16T15:33:30.698Z" }, + { url = "https://files.pythonhosted.org/packages/89/73/8537f99e09a38a54a6a15ede907aa25d4da089f767a808f0b2edd9c03cec/obstore-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a4605c3ed7c9515aeb4c619b5f7f2c9986ed4a79fe6045e536b5e59b804b1476", size = 3460809, upload-time = "2025-09-16T15:33:31.837Z" }, + { url = "https://files.pythonhosted.org/packages/b4/99/7714dec721e43f521d6325a82303a002cddad089437640f92542b84e9cc8/obstore-0.8.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce42670417876dd8668cbb8659e860e9725e5f26bbc86449fd259970e2dd9d18", size = 3692081, upload-time = "2025-09-16T15:33:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/4ac4175fe95a24c220a96021c25c432bcc0c0212f618be0737184eebbaad/obstore-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a3e893b2a06585f651c541c1972fe1e3bf999ae2a5fda052ee55eb7e6516f5", size = 3957466, upload-time = "2025-09-16T15:33:34.528Z" }, + { url = "https://files.pythonhosted.org/packages/4e/04/caa288fb735484fc5cb019bdf3d896eaccfae0ac4622e520d05692c46790/obstore-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08462b32f95a9948ed56ed63e88406e2e5a4cae1fde198f9682e0fb8487100ed", size = 3951293, upload-time = "2025-09-16T15:33:35.733Z" }, + { url = "https://files.pythonhosted.org/packages/44/2f/d380239da2d6a1fda82e17df5dae600a404e8a93a065784518ff8325d5f6/obstore-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a0bf7763292a8fc47d01cd66e6f19002c5c6ad4b3ed4e6b2729f5e190fa8a0d", size = 3766199, upload-time = "2025-09-16T15:33:36.904Z" }, + { url = "https://files.pythonhosted.org/packages/28/41/d391be069d3da82969b54266948b2582aeca5dd735abeda4d63dba36e07b/obstore-0.8.2-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:bcd47f8126cb192cbe86942b8f73b1c45a651ce7e14c9a82c5641dfbf8be7603", size = 3529678, upload-time = "2025-09-16T15:33:38.221Z" }, + { url = "https://files.pythonhosted.org/packages/b9/4c/4862fdd1a3abde459ee8eea699b1797df638a460af235b18ca82c8fffb72/obstore-0.8.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57eda9fd8c757c3b4fe36cf3918d7e589cc1286591295cc10b34122fa36dd3fd", size = 3698079, upload-time = "2025-09-16T15:33:39.696Z" }, + { url = "https://files.pythonhosted.org/packages/68/ca/014e747bc53b570059c27e3565b2316fbe5c107d4134551f4cd3e24aa667/obstore-0.8.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ea44442aad8992166baa69f5069750979e4c5d9ffce772e61565945eea5774b9", size = 3687154, upload-time = "2025-09-16T15:33:40.92Z" }, + { url = "https://files.pythonhosted.org/packages/6f/89/6db5f8edd93028e5b8bfbeee15e6bd3e56f72106107d31cb208b57659de4/obstore-0.8.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:41496a3ab8527402db4142aaaf0d42df9d7d354b13ba10d9c33e0e48dd49dd96", size = 3773444, upload-time = "2025-09-16T15:33:42.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/e5/c9e2cc540689c873beb61246e1615d6e38301e6a34dec424f5a5c63c1afd/obstore-0.8.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43da209803f052df96c7c3cbec512d310982efd2407e4a435632841a51143170", size = 3939315, upload-time = "2025-09-16T15:33:43.252Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c9/bb53280ca50103c1ffda373cdc9b0f835431060039c2897cbc87ddd92e42/obstore-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:1836f5dcd49f9f2950c75889ab5c51fb290d3ea93cdc39a514541e0be3af016e", size = 3978234, upload-time = "2025-09-16T15:33:44.393Z" }, + { url = "https://files.pythonhosted.org/packages/f0/5d/8c3316cc958d386d5e6ab03e9db9ddc27f8e2141cee4a6777ae5b92f3aac/obstore-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:212f033e53fe6e53d64957923c5c88949a400e9027f7038c705ec2e9038be563", size = 3612027, upload-time = "2025-09-16T15:33:45.6Z" }, + { url = "https://files.pythonhosted.org/packages/ea/4d/699359774ce6330130536d008bfc32827fab0c25a00238d015a5974a3d1d/obstore-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bee21fa4ba148d08fa90e47a96df11161661ed31e09c056a373cb2154b0f2852", size = 3344686, upload-time = "2025-09-16T15:33:47.185Z" }, + { url = "https://files.pythonhosted.org/packages/82/37/55437341f10512906e02fd9fa69a8a95ad3f2f6a916d3233fda01763d110/obstore-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c66594b59832ff1ced4c72575d9beb8b5f9b4e404ac1150a42bfb226617fd50", size = 3459860, upload-time = "2025-09-16T15:33:48.382Z" }, + { url = "https://files.pythonhosted.org/packages/7a/51/4245a616c94ee4851965e33f7a563ab4090cc81f52cc73227ff9ceca2e46/obstore-0.8.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:089f33af5c2fe132d00214a0c1f40601b28f23a38e24ef9f79fb0576f2730b74", size = 3691648, upload-time = "2025-09-16T15:33:49.524Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/4e2fb24171e3ca3641a4653f006be826e7e17634b11688a5190553b00b83/obstore-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d87f658dfd340d5d9ea2d86a7c90d44da77a0db9e00c034367dca335735110cf", size = 3956867, upload-time = "2025-09-16T15:33:51.082Z" }, + { url = "https://files.pythonhosted.org/packages/42/f5/b703115361c798c9c1744e1e700d5908d904a8c2e2bd38bec759c9ffb469/obstore-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6e2e4fa92828c4fbc2d487f3da2d3588701a1b67d9f6ca3c97cc2afc912e9c63", size = 3950599, upload-time = "2025-09-16T15:33:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/53/20/08c6dc0f20c1394e2324b9344838e4e7af770cdcb52c30757a475f50daeb/obstore-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab440e89c5c37a8ec230857dd65147d4b923e0cada33297135d05e0f937d696a", size = 3765865, upload-time = "2025-09-16T15:33:53.291Z" }, + { url = "https://files.pythonhosted.org/packages/77/20/77907765e29b2eba6bd8821872284d91170d7084f670855b2dfcb249ea14/obstore-0.8.2-cp313-cp313-manylinux_2_24_aarch64.whl", hash = "sha256:b9beed107c5c9cd995d4a73263861fcfbc414d58773ed65c14f80eb18258a932", size = 3529807, upload-time = "2025-09-16T15:33:54.535Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f5/f629d39cc30d050f52b1bf927e4d65c1cc7d7ffbb8a635cd546b5c5219a0/obstore-0.8.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b75b4e7746292c785e31edcd5aadc8b758238372a19d4c5e394db5c305d7d175", size = 3693629, upload-time = "2025-09-16T15:33:56.016Z" }, + { url = "https://files.pythonhosted.org/packages/30/ff/106763fd10f2a1cb47f2ef1162293c78ad52f4e73223d8d43fc6b755445d/obstore-0.8.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f33e6c366869d05ab0b7f12efe63269e631c5450d95d6b4ba4c5faf63f69de70", size = 3686176, upload-time = "2025-09-16T15:33:57.247Z" }, + { url = "https://files.pythonhosted.org/packages/ce/0c/d2ccb6f32feeca906d5a7c4255340df5262af8838441ca06c9e4e37b67d5/obstore-0.8.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:12c885a9ce5ceb09d13cc186586c0c10b62597eff21b985f6ce8ff9dab963ad3", size = 3773081, upload-time = "2025-09-16T15:33:58.475Z" }, + { url = "https://files.pythonhosted.org/packages/fa/79/40d1cc504cefc89c9b3dd8874287f3fddc7d963a8748d6dffc5880222013/obstore-0.8.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4accc883b93349a81c9931e15dd318cc703b02bbef2805d964724c73d006d00e", size = 3938589, upload-time = "2025-09-16T15:33:59.734Z" }, + { url = "https://files.pythonhosted.org/packages/14/dd/916c6777222db3271e9fb3cf9a97ed92b3a9b3e465bdeec96de9ab809d53/obstore-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ec850adf9980e5788a826ccfd5819989724e2a2f712bfa3258e85966c8d9981e", size = 3977768, upload-time = "2025-09-16T15:34:01.25Z" }, + { url = "https://files.pythonhosted.org/packages/f1/61/66f8dc98bbf5613bbfe5bf21747b4c8091442977f4bd897945895ab7325c/obstore-0.8.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1431e40e9bb4773a261e51b192ea6489d0799b9d4d7dbdf175cdf813eb8c0503", size = 3623364, upload-time = "2025-09-16T15:34:02.957Z" }, + { url = "https://files.pythonhosted.org/packages/1a/66/6d527b3027e42f625c8fc816ac7d19b0d6228f95bfe7666e4d6b081d2348/obstore-0.8.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ddb39d4da303f50b959da000aa42734f6da7ac0cc0be2d5a7838b62c97055bb9", size = 3347764, upload-time = "2025-09-16T15:34:04.236Z" }, + { url = "https://files.pythonhosted.org/packages/0d/79/c00103302b620192ea447a948921ad3fed031ce3d19e989f038e1183f607/obstore-0.8.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e01f4e13783db453e17e005a4a3ceff09c41c262e44649ba169d253098c775e8", size = 3460981, upload-time = "2025-09-16T15:34:05.595Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d9/bfe4ed4b1aebc45b56644dd5b943cf8e1673505cccb352e66878a457e807/obstore-0.8.2-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df0fc2d0bc17caff9b538564ddc26d7616f7e8b7c65b1a3c90b5048a8ad2e797", size = 3692711, upload-time = "2025-09-16T15:34:06.796Z" }, + { url = "https://files.pythonhosted.org/packages/13/47/cd6c2cbb18e1f40c77e7957a4a03d2d83f1859a2e876a408f1ece81cad4c/obstore-0.8.2-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e439d06c99a140348f046c9f598ee349cc2dcd9105c15540a4b231f9cc48bbae", size = 3958362, upload-time = "2025-09-16T15:34:08.277Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ea/5ee82bf23abd71c7d6a3f2d008197ae8f8f569d41314c26a8f75318245be/obstore-0.8.2-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e37d9046669fcc59522d0faf1d105fcbfd09c84cccaaa1e809227d8e030f32c", size = 3957082, upload-time = "2025-09-16T15:34:09.477Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ee/46650405e50fdaa8d95f30375491f9c91fac9517980e8a28a4a6af66927f/obstore-0.8.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2646fdcc4bbe92dc2bb5bcdff15574da1211f5806c002b66d514cee2a23c7cb8", size = 3775539, upload-time = "2025-09-16T15:34:10.726Z" }, + { url = "https://files.pythonhosted.org/packages/35/d6/348a7ebebe2ca3d94dfc75344ea19675ae45472823e372c1852844078307/obstore-0.8.2-cp314-cp314-manylinux_2_24_aarch64.whl", hash = "sha256:e31a7d37675056d93dfc244605089dee67f5bba30f37c88436623c8c5ad9ba9d", size = 3535048, upload-time = "2025-09-16T15:34:12.076Z" }, + { url = "https://files.pythonhosted.org/packages/41/07/b7a16cc0da91a4b902d47880ad24016abfe7880c63f7cdafda45d89a2f91/obstore-0.8.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:656313dd8170dde0f0cd471433283337a63912e8e790a121f7cc7639c83e3816", size = 3699035, upload-time = "2025-09-16T15:34:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/3269a3a58347e0b019742d888612c4b765293c9c75efa44e144b1e884c0d/obstore-0.8.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329038c9645d6d1741e77fe1a53e28a14b1a5c1461cfe4086082ad39ebabf981", size = 3687307, upload-time = "2025-09-16T15:34:14.501Z" }, + { url = "https://files.pythonhosted.org/packages/01/f9/4fd4819ad6a49d2f462a45be453561f4caebded0dc40112deeffc34b89b1/obstore-0.8.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1e4df99b369790c97c752d126b286dc86484ea49bff5782843a265221406566f", size = 3776076, upload-time = "2025-09-16T15:34:16.207Z" }, + { url = "https://files.pythonhosted.org/packages/14/dd/7c4f958fa0b9fc4778fb3d232e38b37db8c6b260f641022fbba48b049d7e/obstore-0.8.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9e1c65c65e20cc990414a8a9af88209b1bbc0dd9521b5f6b0293c60e19439bb7", size = 3947445, upload-time = "2025-09-16T15:34:17.423Z" }, +] + +[[package]] +name = "openai" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/6c/e4c964fcf1d527fdf4739e7cc940c60075a4114d50d03871d5d5b1e13a88/openai-2.16.0.tar.gz", hash = "sha256:42eaa22ca0d8ded4367a77374104d7a2feafee5bd60a107c3c11b5243a11cd12", size = 629649, upload-time = "2026-01-27T23:28:02.579Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/83/0315bf2cfd75a2ce8a7e54188e9456c60cec6c0cf66728ed07bd9859ff26/openai-2.16.0-py3-none-any.whl", hash = "sha256:5f46643a8f42899a84e80c38838135d7038e7718333ce61396994f887b09a59b", size = 1068612, upload-time = "2026-01-27T23:28:00.356Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "portkey-ai" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "cached-property" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "types-requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/8a/f5bbaab806ad61d9959cb7c88c639200feacac1b2ba7b455b97a2f216e7c/portkey_ai-2.1.0.tar.gz", hash = "sha256:c2558041c568eef8528737978089301cb9be056f166a683251831cbfa6a623cb", size = 567417, upload-time = "2025-11-25T20:32:43.102Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/11/c585b90ac842027e5f4f7f7cee72d3197f58ff24b6d7c5f1243aa8fa96be/portkey_ai-2.1.0-py3-none-any.whl", hash = "sha256:2166033f8e198745947fee5321d0bbcfb005afc35468bd5a948fa83dc16b6767", size = 1181622, upload-time = "2025-11-25T20:32:41.185Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, +] + +[[package]] +name = "prime-sandboxes" +version = "0.2.14" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "tenacity" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/aa/e6f8e67aa0dc712e84f2c0280cb167e385b6527416405c74a67151e08e2e/prime_sandboxes-0.2.14.tar.gz", hash = "sha256:fbdfc80b1b00e16d2c7448d735f2080b42a51672cd8c6e937b44668348b3d640", size = 47506, upload-time = "2026-02-01T08:24:41.288Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4c/9289d7e06e9307b0476287234576013703e09e12a76d402b1e827f1b3dd4/prime_sandboxes-0.2.14-py3-none-any.whl", hash = "sha256:9ca2067c0d1e970ee71e2be9f0be53fe0ef6c87c54fcb24ba1ef71374a173d08", size = 20820, upload-time = "2026-02-01T08:24:39.822Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, + { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, + { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, + { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/b6/6e630dff89739fcd427e3f72b3d905ce0acb85a45d4ec3e2678718a3487f/pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b", size = 146586, upload-time = "2026-01-16T18:04:18.534Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/b5/a96872e5184f354da9c84ae119971a0a4c221fe9b27a4d94bd43f2596727/pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf", size = 83371, upload-time = "2026-01-16T18:04:17.174Z" }, +] + +[[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, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rich" +version = "14.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/99/a4cab2acbb884f80e558b0771e97e21e939c5dfb460f488d19df485e8298/rich-14.3.2.tar.gz", hash = "sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8", size = 230143, upload-time = "2026-02-01T16:20:47.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/45/615f5babd880b4bd7d405cc0dc348234c5ffb6ed1ea33e152ede08b2072d/rich-14.3.2-py3-none-any.whl", hash = "sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69", size = 309963, upload-time = "2026-02-01T16:20:46.078Z" }, +] + +[[package]] +name = "rlm" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "anthropic" }, + { name = "google-genai" }, + { name = "openai" }, + { name = "portkey-ai" }, + { name = "pytest" }, + { name = "python-dotenv" }, + { name = "requests" }, + { name = "rich" }, +] + +[package.optional-dependencies] +daytona = [ + { name = "daytona" }, + { name = "dill" }, +] +modal = [ + { name = "dill" }, + { name = "modal" }, +] +prime = [ + { name = "dill" }, + { name = "prime-sandboxes" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pre-commit" }, + { name = "ruff" }, + { name = "ty" }, +] +test = [ + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, +] + +[package.metadata] +requires-dist = [ + { name = "anthropic", specifier = ">=0.75.0" }, + { name = "daytona", marker = "extra == 'daytona'", specifier = ">=0.128.1" }, + { name = "dill", marker = "extra == 'daytona'", specifier = ">=0.3.7" }, + { name = "dill", marker = "extra == 'modal'", specifier = ">=0.3.7" }, + { name = "dill", marker = "extra == 'prime'", specifier = ">=0.3.7" }, + { name = "google-genai", specifier = ">=1.0.0" }, + { name = "modal", marker = "extra == 'modal'", specifier = ">=0.73.0" }, + { name = "openai", specifier = ">=2.14.0" }, + { name = "portkey-ai", specifier = ">=2.1.0" }, + { name = "prime-sandboxes", marker = "extra == 'prime'", specifier = ">=0.2.0" }, + { name = "pytest", specifier = ">=9.0.2" }, + { name = "python-dotenv", specifier = ">=1.2.1" }, + { name = "requests", specifier = ">=2.32.5" }, + { name = "rich", specifier = ">=13.0.0" }, +] +provides-extras = ["modal", "daytona", "prime"] + +[package.metadata.requires-dev] +dev = [ + { name = "pre-commit", specifier = ">=4.5.1" }, + { name = "ruff", specifier = ">=0.14.10" }, + { name = "ty", specifier = ">=0.0.7" }, +] +test = [ + { name = "pytest", specifier = ">=8.0.0" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, + { name = "pytest-cov", specifier = ">=6.0.0" }, +] + +[[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, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "ruff" +version = "0.14.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/06/f71e3a86b2df0dfa2d2f72195941cd09b44f87711cb7fa5193732cb9a5fc/ruff-0.14.14.tar.gz", hash = "sha256:2d0f819c9a90205f3a867dbbd0be083bee9912e170fd7d9704cc8ae45824896b", size = 4515732, upload-time = "2026-01-22T22:30:17.527Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/89/20a12e97bc6b9f9f68343952da08a8099c57237aef953a56b82711d55edd/ruff-0.14.14-py3-none-linux_armv6l.whl", hash = "sha256:7cfe36b56e8489dee8fbc777c61959f60ec0f1f11817e8f2415f429552846aed", size = 10467650, upload-time = "2026-01-22T22:30:08.578Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b1/c5de3fd2d5a831fcae21beda5e3589c0ba67eec8202e992388e4b17a6040/ruff-0.14.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6006a0082336e7920b9573ef8a7f52eec837add1265cc74e04ea8a4368cd704c", size = 10883245, upload-time = "2026-01-22T22:30:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7c/3c1db59a10e7490f8f6f8559d1db8636cbb13dccebf18686f4e3c9d7c772/ruff-0.14.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:026c1d25996818f0bf498636686199d9bd0d9d6341c9c2c3b62e2a0198b758de", size = 10231273, upload-time = "2026-01-22T22:30:34.642Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6e/5e0e0d9674be0f8581d1f5e0f0a04761203affce3232c1a1189d0e3b4dad/ruff-0.14.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f666445819d31210b71e0a6d1c01e24447a20b85458eea25a25fe8142210ae0e", size = 10585753, upload-time = "2026-01-22T22:30:31.781Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/754ab09f46ff1884d422dc26d59ba18b4e5d355be147721bb2518aa2a014/ruff-0.14.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c0f18b922c6d2ff9a5e6c3ee16259adc513ca775bcf82c67ebab7cbd9da5bc8", size = 10286052, upload-time = "2026-01-22T22:30:24.827Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cc/e71f88dd2a12afb5f50733851729d6b571a7c3a35bfdb16c3035132675a0/ruff-0.14.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1629e67489c2dea43e8658c3dba659edbfd87361624b4040d1df04c9740ae906", size = 11043637, upload-time = "2026-01-22T22:30:13.239Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/397245026352494497dac935d7f00f1468c03a23a0c5db6ad8fc49ca3fb2/ruff-0.14.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:27493a2131ea0f899057d49d303e4292b2cae2bb57253c1ed1f256fbcd1da480", size = 12194761, upload-time = "2026-01-22T22:30:22.542Z" }, + { url = "https://files.pythonhosted.org/packages/5b/06/06ef271459f778323112c51b7587ce85230785cd64e91772034ddb88f200/ruff-0.14.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ff589aab3f5b539e35db38425da31a57521efd1e4ad1ae08fc34dbe30bd7df", size = 12005701, upload-time = "2026-01-22T22:30:20.499Z" }, + { url = "https://files.pythonhosted.org/packages/41/d6/99364514541cf811ccc5ac44362f88df66373e9fec1b9d1c4cc830593fe7/ruff-0.14.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc12d74eef0f29f51775f5b755913eb523546b88e2d733e1d701fe65144e89b", size = 11282455, upload-time = "2026-01-22T22:29:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/ca/71/37daa46f89475f8582b7762ecd2722492df26421714a33e72ccc9a84d7a5/ruff-0.14.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb8481604b7a9e75eff53772496201690ce2687067e038b3cc31aaf16aa0b974", size = 11215882, upload-time = "2026-01-22T22:29:57.032Z" }, + { url = "https://files.pythonhosted.org/packages/2c/10/a31f86169ec91c0705e618443ee74ede0bdd94da0a57b28e72db68b2dbac/ruff-0.14.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14649acb1cf7b5d2d283ebd2f58d56b75836ed8c6f329664fa91cdea19e76e66", size = 11180549, upload-time = "2026-01-22T22:30:27.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1e/c723f20536b5163adf79bdd10c5f093414293cdf567eed9bdb7b83940f3f/ruff-0.14.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e8058d2145566510790eab4e2fad186002e288dec5e0d343a92fe7b0bc1b3e13", size = 10543416, upload-time = "2026-01-22T22:30:01.964Z" }, + { url = "https://files.pythonhosted.org/packages/3e/34/8a84cea7e42c2d94ba5bde1d7a4fae164d6318f13f933d92da6d7c2041ff/ruff-0.14.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e651e977a79e4c758eb807f0481d673a67ffe53cfa92209781dfa3a996cf8412", size = 10285491, upload-time = "2026-01-22T22:30:29.51Z" }, + { url = "https://files.pythonhosted.org/packages/55/ef/b7c5ea0be82518906c978e365e56a77f8de7678c8bb6651ccfbdc178c29f/ruff-0.14.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cc8b22da8d9d6fdd844a68ae937e2a0adf9b16514e9a97cc60355e2d4b219fc3", size = 10733525, upload-time = "2026-01-22T22:30:06.499Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/aaf1dfbcc53a2811f6cc0a1759de24e4b03e02ba8762daabd9b6bd8c59e3/ruff-0.14.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:16bc890fb4cc9781bb05beb5ab4cd51be9e7cb376bf1dd3580512b24eb3fda2b", size = 11315626, upload-time = "2026-01-22T22:30:36.848Z" }, + { url = "https://files.pythonhosted.org/packages/2c/aa/9f89c719c467dfaf8ad799b9bae0df494513fb21d31a6059cb5870e57e74/ruff-0.14.14-py3-none-win32.whl", hash = "sha256:b530c191970b143375b6a68e6f743800b2b786bbcf03a7965b06c4bf04568167", size = 10502442, upload-time = "2026-01-22T22:30:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/87/44/90fa543014c45560cae1fffc63ea059fb3575ee6e1cb654562197e5d16fb/ruff-0.14.14-py3-none-win_amd64.whl", hash = "sha256:3dde1435e6b6fe5b66506c1dff67a421d0b7f6488d466f651c07f4cab3bf20fd", size = 11630486, upload-time = "2026-01-22T22:30:10.852Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6a/40fee331a52339926a92e17ae748827270b288a35ef4a15c9c8f2ec54715/ruff-0.14.14-py3-none-win_arm64.whl", hash = "sha256:56e6981a98b13a32236a72a8da421d7839221fa308b223b9283312312e5ac76c", size = 10920448, upload-time = "2026-01-22T22:30:15.417Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[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, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "synchronicity" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/8874d34755691994266d4a844ba8d53d10c2690ec67f246ca4d6b6f34cbb/synchronicity-0.11.1.tar.gz", hash = "sha256:3628df9ab34bd7be89b729104114841c62612c5d5ec43b76f4b7b243185ec1a8", size = 58131, upload-time = "2025-12-19T18:28:42.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/b9/71153db12f4ad029cfe9b7fbf9792ef3fc9ade4485d31a13470b52954e62/synchronicity-0.11.1-py3-none-any.whl", hash = "sha256:53959c7f8b9b852fb5ea4d3d290a47a04310ede483a4cf0f8452cb4b5fa09db2", size = 40399, upload-time = "2025-12-19T18:28:40.972Z" }, +] + +[[package]] +name = "tenacity" +version = "9.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/89/4b0001b2dab8df0a5ee2787dcbe771de75ded01f18f1f8d53dedeea2882b/tqdm-4.67.2.tar.gz", hash = "sha256:649aac53964b2cb8dec76a14b405a4c0d13612cb8933aae547dd144eacc99653", size = 169514, upload-time = "2026-01-30T23:12:06.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/e2/31eac96de2915cf20ccaed0225035db149dfb9165a9ed28d4b252ef3f7f7/tqdm-4.67.2-py3-none-any.whl", hash = "sha256:9a12abcbbff58b6036b2167d9d3853042b9d436fe7330f06ae047867f2f8e0a7", size = 78354, upload-time = "2026-01-30T23:12:04.368Z" }, +] + +[[package]] +name = "ty" +version = "0.0.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/57/22c3d6bf95c2229120c49ffc2f0da8d9e8823755a1c3194da56e51f1cc31/ty-0.0.14.tar.gz", hash = "sha256:a691010565f59dd7f15cf324cdcd1d9065e010c77a04f887e1ea070ba34a7de2", size = 5036573, upload-time = "2026-01-27T00:57:31.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/cb/cc6d1d8de59beb17a41f9a614585f884ec2d95450306c173b3b7cc090d2e/ty-0.0.14-py3-none-linux_armv6l.whl", hash = "sha256:32cf2a7596e693094621d3ae568d7ee16707dce28c34d1762947874060fdddaa", size = 10034228, upload-time = "2026-01-27T00:57:53.133Z" }, + { url = "https://files.pythonhosted.org/packages/f3/96/dd42816a2075a8f31542296ae687483a8d047f86a6538dfba573223eaf9a/ty-0.0.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f971bf9805f49ce8c0968ad53e29624d80b970b9eb597b7cbaba25d8a18ce9a2", size = 9939162, upload-time = "2026-01-27T00:57:43.857Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b4/73c4859004e0f0a9eead9ecb67021438b2e8e5fdd8d03e7f5aca77623992/ty-0.0.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:45448b9e4806423523268bc15e9208c4f3f2ead7c344f615549d2e2354d6e924", size = 9418661, upload-time = "2026-01-27T00:58:03.411Z" }, + { url = "https://files.pythonhosted.org/packages/58/35/839c4551b94613db4afa20ee555dd4f33bfa7352d5da74c5fa416ffa0fd2/ty-0.0.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee94a9b747ff40114085206bdb3205a631ef19a4d3fb89e302a88754cbbae54c", size = 9837872, upload-time = "2026-01-27T00:57:23.718Z" }, + { url = "https://files.pythonhosted.org/packages/41/2b/bbecf7e2faa20c04bebd35fc478668953ca50ee5847ce23e08acf20ea119/ty-0.0.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6756715a3c33182e9ab8ffca2bb314d3c99b9c410b171736e145773ee0ae41c3", size = 9848819, upload-time = "2026-01-27T00:57:58.501Z" }, + { url = "https://files.pythonhosted.org/packages/be/60/3c0ba0f19c0f647ad9d2b5b5ac68c0f0b4dc899001bd53b3a7537fb247a2/ty-0.0.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89d0038a2f698ba8b6fec5cf216a4e44e2f95e4a5095a8c0f57fe549f87087c2", size = 10324371, upload-time = "2026-01-27T00:57:29.291Z" }, + { url = "https://files.pythonhosted.org/packages/24/32/99d0a0b37d0397b0a989ffc2682493286aa3bc252b24004a6714368c2c3d/ty-0.0.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c64a83a2d669b77f50a4957039ca1450626fb474619f18f6f8a3eb885bf7544", size = 10865898, upload-time = "2026-01-27T00:57:33.542Z" }, + { url = "https://files.pythonhosted.org/packages/1a/88/30b583a9e0311bb474269cfa91db53350557ebec09002bfc3fb3fc364e8c/ty-0.0.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:242488bfb547ef080199f6fd81369ab9cb638a778bb161511d091ffd49c12129", size = 10555777, upload-time = "2026-01-27T00:58:05.853Z" }, + { url = "https://files.pythonhosted.org/packages/cd/a2/cb53fb6325dcf3d40f2b1d0457a25d55bfbae633c8e337bde8ec01a190eb/ty-0.0.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4790c3866f6c83a4f424fc7d09ebdb225c1f1131647ba8bdc6fcdc28f09ed0ff", size = 10412913, upload-time = "2026-01-27T00:57:38.834Z" }, + { url = "https://files.pythonhosted.org/packages/42/8f/f2f5202d725ed1e6a4e5ffaa32b190a1fe70c0b1a2503d38515da4130b4c/ty-0.0.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:950f320437f96d4ea9a2332bbfb5b68f1c1acd269ebfa4c09b6970cc1565bd9d", size = 9837608, upload-time = "2026-01-27T00:57:55.898Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ba/59a2a0521640c489dafa2c546ae1f8465f92956fede18660653cce73b4c5/ty-0.0.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4a0ec3ee70d83887f86925bbc1c56f4628bd58a0f47f6f32ddfe04e1f05466df", size = 9884324, upload-time = "2026-01-27T00:57:46.786Z" }, + { url = "https://files.pythonhosted.org/packages/03/95/8d2a49880f47b638743212f011088552ecc454dd7a665ddcbdabea25772a/ty-0.0.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1a4e6b6da0c58b34415955279eff754d6206b35af56a18bb70eb519d8d139ef", size = 10033537, upload-time = "2026-01-27T00:58:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/e9/40/4523b36f2ce69f92ccf783855a9e0ebbbd0f0bb5cdce6211ee1737159ed3/ty-0.0.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dc04384e874c5de4c5d743369c277c8aa73d1edea3c7fc646b2064b637db4db3", size = 10495910, upload-time = "2026-01-27T00:57:26.691Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/655beb51224d1bfd4f9ddc0bb209659bfe71ff141bcf05c418ab670698f0/ty-0.0.14-py3-none-win32.whl", hash = "sha256:b20e22cf54c66b3e37e87377635da412d9a552c9bf4ad9fc449fed8b2e19dad2", size = 9507626, upload-time = "2026-01-27T00:57:41.43Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d9/c569c9961760e20e0a4bc008eeb1415754564304fd53997a371b7cf3f864/ty-0.0.14-py3-none-win_amd64.whl", hash = "sha256:e312ff9475522d1a33186657fe74d1ec98e4a13e016d66f5758a452c90ff6409", size = 10437980, upload-time = "2026-01-27T00:57:36.422Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0c/186829654f5bfd9a028f6648e9caeb11271960a61de97484627d24443f91/ty-0.0.14-py3-none-win_arm64.whl", hash = "sha256:b6facdbe9b740cb2c15293a1d178e22ffc600653646452632541d01c36d5e378", size = 9885831, upload-time = "2026-01-27T00:57:49.747Z" }, +] + +[[package]] +name = "typer" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" }, +] + +[[package]] +name = "types-certifi" +version = "2021.10.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095, upload-time = "2022-06-09T15:19:05.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136, upload-time = "2022-06-09T15:19:03.127Z" }, +] + +[[package]] +name = "types-requests" +version = "2.32.4.20260107" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165, upload-time = "2026-01-07T03:20:54.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392, upload-time = "2024-03-10T02:18:37.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777, upload-time = "2024-03-10T02:18:36.568Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.36.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, + { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, + { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, + { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, + { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, + { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, + { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, + { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, + { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, +] + +[[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, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, + { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, + { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, + { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, + { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, + { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, + { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, + { 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, upload-time = "2025-03-05T20:02:36.695Z" }, + { 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, upload-time = "2025-03-05T20:02:37.985Z" }, + { 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, upload-time = "2025-03-05T20:02:39.298Z" }, + { 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, upload-time = "2025-03-05T20:02:40.595Z" }, + { 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, upload-time = "2025-03-05T20:02:41.926Z" }, + { 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, upload-time = "2025-03-05T20:02:43.304Z" }, + { 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, upload-time = "2025-03-05T20:02:48.812Z" }, + { 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, upload-time = "2025-03-05T20:02:50.14Z" }, + { 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, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] + +[[package]] +name = "wrapt" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/31/afb4cf08b9892430ec419a3f0f469fb978cb013f4432e0edb9c2cf06f081/wrapt-2.1.0.tar.gz", hash = "sha256:757ff1de7e1d8db1839846672aaecf4978af433cc57e808255b83980e9651914", size = 80924, upload-time = "2026-01-31T23:25:58.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/0a/de541b2543e33144043cd58da09bda8d837ba42e13ae90baca32b0553023/wrapt-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d877003dbc601e1365bd03f6a980965a20d585f90c056f33e1fc241b63a6f0e7", size = 60558, upload-time = "2026-01-31T23:25:27.784Z" }, + { url = "https://files.pythonhosted.org/packages/84/2e/7e48207420e6ca7e7a05c0e4ebe9464ec9965c8face256f3ef8cc2acd862/wrapt-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:771ec962fe3ccb078177c9b8f3529e204ffcbb11d62d509e0a438e6a83f7ca68", size = 61501, upload-time = "2026-01-31T23:26:46.477Z" }, + { url = "https://files.pythonhosted.org/packages/67/2b/639a4970ecdc7143acb69a1162c76b0f1620218ad502c33e1a88d28f00b1/wrapt-2.1.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73e742368b52f9cf0921e1d2bcb8a6a44ede2e372e33df6e77caa136a942099f", size = 113954, upload-time = "2026-01-31T23:26:01.493Z" }, + { url = "https://files.pythonhosted.org/packages/81/5d/8d9177c8c0ecaf5313b462be63c5aa9672044b02bfd644dd65c6cb420d2a/wrapt-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e9129d1b582c55ad0dfb9e29e221daa0e02b18c67d8642bc8d08dd7038b3aed", size = 115994, upload-time = "2026-01-31T23:25:57.118Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e3/c5a514a0ed1dc463f5b6b4e31abbaa3b8df48b9fd391a6e8412608155a29/wrapt-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc9e37bfe67f6ea738851dd606640a87692ff81bcc76df313fb75d08e05e855f", size = 115245, upload-time = "2026-01-31T23:26:11.171Z" }, + { url = "https://files.pythonhosted.org/packages/35/9c/2fc6a31f5758266de2cf9dc6111d3bda7b7dd6cbdcabfd755103bbcda08f/wrapt-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:46583aae3c807aa76f96355c4943031225785ed160c84052612bba0e9d456639", size = 113679, upload-time = "2026-01-31T23:25:19.475Z" }, + { url = "https://files.pythonhosted.org/packages/6c/81/ce52694dc8184f4898c01c8af20e145b348fc7a0e4766a7345c45f0e9ce6/wrapt-2.1.0-cp311-cp311-win32.whl", hash = "sha256:e3958ba70aef2895d8c62c2d31f51ced188f60451212294677b92f4b32c12978", size = 57865, upload-time = "2026-01-31T23:25:50.947Z" }, + { url = "https://files.pythonhosted.org/packages/85/31/0df5d38243c2a538e7bd481e676d286b41f98a729e0d37cfed9f4421ad4d/wrapt-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0ff9797e6e0b82b330ef80b0cdba7fcd0ca056d4c7af2ca44e3d05fd47929ede", size = 60227, upload-time = "2026-01-31T23:25:35.954Z" }, + { url = "https://files.pythonhosted.org/packages/a3/79/b587edbab21d6b8a7460234440c784e08344bcdf4fdfd9a6e9125ea14923/wrapt-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:4b0a29509ef7b501abe47b693a3c91d1f21c9a948711f6ce7afa81eb274c7eae", size = 58648, upload-time = "2026-01-31T23:25:32.887Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6f/c731b1fbbcdf9bd202809c6fa354c4237b663dd82a95035a7cbe899cfd25/wrapt-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a64c0fb29c89810973f312a04c067b63523e7303b9a2653820cbf16474c2e5cf", size = 61149, upload-time = "2026-01-31T23:25:29.092Z" }, + { url = "https://files.pythonhosted.org/packages/b2/da/7022458a1d99f0c59720a0b0fd4b1966f8df6d41e741aadfe43bc5350547/wrapt-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5509d9150ed01c4149e40020fa68e917d5c4bb77d311e79535565c2a0418afcb", size = 61743, upload-time = "2026-01-31T23:26:14.338Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f4/57cc12c3fc6f4fe6ccfc15567cc1ac8aeb53a9946a675adc3df7a1ee4e6a/wrapt-2.1.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:52bb58b3207ace156b6134235fd43140994597704fd07d148cbcfb474ee084ea", size = 121331, upload-time = "2026-01-31T23:25:37.294Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a4/a96ea114298f81f02c07313da85fd46a2a57bbe12389d0619ac3371f691c/wrapt-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7112cbf72fc4035afe1e3314a311654c41dd92c2932021ef76f5ca87583917b3", size = 122907, upload-time = "2026-01-31T23:26:49.604Z" }, + { url = "https://files.pythonhosted.org/packages/ac/43/df73362b6e47f92aaff0fc3fc459314025c795f75d61724c83232dee199c/wrapt-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e90656b433808a0ab68e95aaf9f588aea5c8c7a514e180849dfc638ba00ec449", size = 121337, upload-time = "2026-01-31T23:26:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/51/4f/8147e3b9a7887cee4eeb3a3414265ad4649a156832a08063f55aa7842af0/wrapt-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e45f54903da38fc4f6f66397fd550fc0dac6164b4c5e721c1b4eb05664181821", size = 120461, upload-time = "2026-01-31T23:26:43.055Z" }, + { url = "https://files.pythonhosted.org/packages/35/b1/eea720fcca8a05dec848a6d11a47c20f59bdabdcc444ba3be0589350eb7a/wrapt-2.1.0-cp312-cp312-win32.whl", hash = "sha256:6653bf30dbbafd55cb4553195cc60b94920b6711a8835866c0e02aa9f22c5598", size = 58089, upload-time = "2026-01-31T23:26:47.773Z" }, + { url = "https://files.pythonhosted.org/packages/af/79/8a8f3f8c71ee3379191b69e47f32115fa25cdb6d5b581d74c64d5c897fa7/wrapt-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:d61238a072501ed071a9f4b9567d10c2eb3d2f1a0258ae79b47160871d8f29c3", size = 60330, upload-time = "2026-01-31T23:26:12.518Z" }, + { url = "https://files.pythonhosted.org/packages/08/4e/e992d05c3d2f7163883a65ead2620ff5fe7b3d44d7c2136ce981e40e453d/wrapt-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:9e971000347f61271725e801ef44fa5d01b52720e59737f0d96280bffb98c5d1", size = 58727, upload-time = "2026-01-31T23:26:53.222Z" }, + { url = "https://files.pythonhosted.org/packages/30/93/b414826a5aaf2fdcfe73c2e649cbeb2e098fef4820d1217554ee64f45666/wrapt-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:875a10a6f3b667f90a39010af26acf684ba831d9b18a86b242899d57c74550fa", size = 61155, upload-time = "2026-01-31T23:26:24.462Z" }, + { url = "https://files.pythonhosted.org/packages/58/9e/8b21ea776bf2a3c858e3377ecde4b348893ec44dc1726baaf583ca22c56e/wrapt-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e00f8559ceac0fb45091daad5f15d37f2c22bdc28ed71521d47ff01aad8fff3d", size = 61747, upload-time = "2026-01-31T23:25:53.987Z" }, + { url = "https://files.pythonhosted.org/packages/da/ec/48cd2470ad09557dfe6fccfe9de98698cc0df3786a6d4d97e8edd574d67a/wrapt-2.1.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ce0cf4c79c19904aaf2e822af280d7b3c23ad902f57e31c5a19433bc86e5d36d", size = 121342, upload-time = "2026-01-31T23:26:32.156Z" }, + { url = "https://files.pythonhosted.org/packages/3b/4e/e8447b31be27b6057cdfc904a38632a765c3407fb4d10d11e5c1d0c203d5/wrapt-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3dd4f8c2256fcde1a85037a1837afc52e8d32d086fd669ae469455fd9a988d6", size = 122951, upload-time = "2026-01-31T23:25:08.936Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b6/73a6c9277e844ffe11f3002ad27a84ff5418248def33af9435d24dfe6c5b/wrapt-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:737e1e491473047cb66944b8b8fd23f3f542019afd6cf0569d1356d18a7ea6d5", size = 121373, upload-time = "2026-01-31T23:26:18.322Z" }, + { url = "https://files.pythonhosted.org/packages/85/04/869384435fecf829dc05621ffa02dab0f2f830be5d42fa8d8ac7b0b4c9fa/wrapt-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38de19e30e266c15d542ceb0603e657db4e82c53e7f47fd70674ae5da2b41180", size = 120468, upload-time = "2026-01-31T23:25:13.689Z" }, + { url = "https://files.pythonhosted.org/packages/80/ac/42a5378d9b5b486122ae0572c46ae8d69ab6486b9f13961e6b9706297ff5/wrapt-2.1.0-cp313-cp313-win32.whl", hash = "sha256:bc7d496b6e16bd2f77e37e8969b21a7b58d6954e46c6689986fb67b9078100e5", size = 58095, upload-time = "2026-01-31T23:26:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/86/de/538fcef30f70a1aaadab4cab7d0396037518d7ec2b064557171147ce297f/wrapt-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:57df799e67b011847ef7ac64b05ed4633e56b64e7e7cab5eb83dc9689dbe0acf", size = 60344, upload-time = "2026-01-31T23:25:10.615Z" }, + { url = "https://files.pythonhosted.org/packages/08/13/27884668b21e9f0a625c13ebd6a8d70ad8371250ec8519881858404686bf/wrapt-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:01559d2961c29edc6263849fd9d32b29a20737da67648c7fd752a67bd96208c7", size = 58734, upload-time = "2026-01-31T23:26:00.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a3/e558c5b8f3a097aa1e942e2d75923adebfdfafb5a51ec425d1d062e49ab0/wrapt-2.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:66f588c8b3a44863156cfaccb516f946a64b3b03a6880822ab0b878135ca1f5c", size = 62972, upload-time = "2026-01-31T23:26:08.576Z" }, + { url = "https://files.pythonhosted.org/packages/93/b6/7157e98107099fad846f1e79308cc0954e26b25b01c03f1624ba7f57ec54/wrapt-2.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:355779ff720c11a2a5cffd03332dbce1005cb4747dca65b0fc8cdd5f8bf1037e", size = 63610, upload-time = "2026-01-31T23:26:39.9Z" }, + { url = "https://files.pythonhosted.org/packages/e4/8e/b8992671e4b4d3ce2a53af930588c204bf37b66eb212bd1722f2a5a8cf62/wrapt-2.1.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7a0471df3fb4e85a9ff62f7142cdb169e31172467cdb79a713f9b1319c555903", size = 152538, upload-time = "2026-01-31T23:26:27.696Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f6/79f9fd4b3c0a8715e651fff1cc1182a983fd971376d5688a06fa94e31acd/wrapt-2.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5bacf063143fa86f15b00a21259a81c95c527a18d504b8c820835366d361c879", size = 158702, upload-time = "2026-01-31T23:25:11.848Z" }, + { url = "https://files.pythonhosted.org/packages/9e/46/f88b52beb813eeb830d9134bc6eaf3e53cde4e3cfa1804e383754d4104fe/wrapt-2.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c87cd4f61a3b7cd65113e74006e1cd6352b74807fcc65d440e8342f001f8de5e", size = 155564, upload-time = "2026-01-31T23:25:15.033Z" }, + { url = "https://files.pythonhosted.org/packages/93/31/97145ea71e3e5a1b419af5c410b07b258155dc7cc1a6302791a93e991c83/wrapt-2.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2893498fe898719ac8fb6b4fe36ca86892bec1e2480d94e3bd1bc592c00527ad", size = 150165, upload-time = "2026-01-31T23:26:09.848Z" }, + { url = "https://files.pythonhosted.org/packages/10/bd/f33551d5bfbb0ddab81296cffc15570570039a973c0f99bba474be0fadf2/wrapt-2.1.0-cp313-cp313t-win32.whl", hash = "sha256:cbc07f101f5f1e7c23ec06a07e45715f459de992108eeb381b21b76d94dbaf4f", size = 59785, upload-time = "2026-01-31T23:25:52.23Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3a/9a76be7a36442f43841bb6336e262e09a915b2fb5dfc2822ffce1fb903d2/wrapt-2.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2ccc89cd504fc29c32f0b24046e8edf3ef0fcbc5d5efe8c91b303c099863d2c8", size = 63085, upload-time = "2026-01-31T23:26:05.363Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/65a13c2df008d189ebca5fec534011c5dd69ab4f47e6923b403321816fbf/wrapt-2.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:0b660be1c9cdfb4c711baab4ccbd0e9d1b65a0480d38729ec8cdbf3b29cb7f15", size = 60254, upload-time = "2026-01-31T23:25:06.052Z" }, + { url = "https://files.pythonhosted.org/packages/6f/eb/7c9eb1ea9b10ea98d9983a147c877a2ae927acb4a86e2dc4a0b548f05ad1/wrapt-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7f7bf95bae7ac5f2bbcb307464b3b0ff70569dd3b036a87b1cf7efb2c76e66e5", size = 61316, upload-time = "2026-01-31T23:25:20.739Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c2/1c3d16d6b644f688913a00e2dc10f59adca817b5b3ee034ce4e9a692ab63/wrapt-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:be2f541a242818829526e5d08c716b6730970ed0dc1b76ba962a546947d0f005", size = 61813, upload-time = "2026-01-31T23:25:49.714Z" }, + { url = "https://files.pythonhosted.org/packages/8c/51/b6170084b6b771cc62374d924e328df2e81f687399a835f003497cad1110/wrapt-2.1.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad3aa174d06a14b4758d5a1678b9adde8b8e657c6695de9a3d4c223f4fcbbcce", size = 120309, upload-time = "2026-01-31T23:25:16.866Z" }, + { url = "https://files.pythonhosted.org/packages/f8/34/467829f0dd79f50878b2e67b67c67c816a6326a27d252d4192ef815b4a09/wrapt-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bffa584240d41bc3127510e07a752f94223d73bb1283ac2e99ac44235762efd2", size = 122690, upload-time = "2026-01-31T23:26:16.914Z" }, + { url = "https://files.pythonhosted.org/packages/df/5b/244c61a65e0bc9d4a18cfa2a2b3b05f8065290284fc60436a7ea5047ee10/wrapt-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9b2da9c8f1723994b335dbf9f496fbfabc76bcdd001f73772b8eb2118a714cea", size = 121115, upload-time = "2026-01-31T23:26:44.518Z" }, + { url = "https://files.pythonhosted.org/packages/86/7d/f9b5e103d3caf23a72c04a1baf2b61c4a14d1feb440d3c98c26725b4503a/wrapt-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:eabe95ea5fbe1524a53c0f3fc535c99f2aa376ec1451b0b79d943d2240d80e36", size = 119487, upload-time = "2026-01-31T23:25:34.186Z" }, + { url = "https://files.pythonhosted.org/packages/f8/49/b61fdc4680dd5cd6828977341b9fd729e2c623338bfe65647f5c0ff8195e/wrapt-2.1.0-cp314-cp314-win32.whl", hash = "sha256:2cd647097df1df78f027ac7d5d663f05daa1a117b69cf7f476cb299f90557747", size = 58519, upload-time = "2026-01-31T23:25:04.426Z" }, + { url = "https://files.pythonhosted.org/packages/6a/4f/42ab43e496d0d19caed9f69366d0f28f7f08c139297e78b17dab6ecbb6d5/wrapt-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0fc3e388a14ef8101c685dc80b4d2932924a639a03e5c44b5ffabbda2f1f2dc", size = 60767, upload-time = "2026-01-31T23:25:21.954Z" }, + { url = "https://files.pythonhosted.org/packages/ef/15/0337768ac97a8758bc0fc1afdf5f656075a7facf198f62bbe8a22b789277/wrapt-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7c06653908a23a85c4b2455b9d37c085f9756c09058df87b4a2fce2b2f8d58c2", size = 59056, upload-time = "2026-01-31T23:26:25.814Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f1/58f4674d1db44912003a51b34e8d9823a832fbbb39162e9dbe06e5f6424e/wrapt-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c70b4829c6f2f4af4cdaa16442032fcaf882063304160555e4a19b43fd2c6c9d", size = 63061, upload-time = "2026-01-31T23:26:06.601Z" }, + { url = "https://files.pythonhosted.org/packages/02/c1/07f6bf6619285f39cd616314217170c6160da99a46ad6ae4a60044f6ab5a/wrapt-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d7fd4c4ee51ebdf245549d54a7c2181a4f39caac97c9dc8a050b5ba814067a29", size = 63620, upload-time = "2026-01-31T23:25:30.326Z" }, + { url = "https://files.pythonhosted.org/packages/46/82/f7df1648762260f60c4e22c066a17d95f20267c94bfe653fab4f08e2c297/wrapt-2.1.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7b158558438874e5fd5cb505b5a635bd08c84857bc937973d9e12e1166cdf3b", size = 152546, upload-time = "2026-01-31T23:25:02.102Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/d953336e09bac13a9ffa9073e167c5dec8aaa4a717a8551bf64cb4683590/wrapt-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e2e156fe2d41700b837be9b1d8d80ebab44e9891589bc7c41578ef110184e29", size = 158704, upload-time = "2026-01-31T23:25:43.269Z" }, + { url = "https://files.pythonhosted.org/packages/39/a1/2ed57e46b30af2a5a750c85a9dd30d2244ef10e2f8db150560126d8cbd24/wrapt-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9f1e9bac6a6c1ba65e0ac50e32c575266734a07b6c17e718c4babd91e2faa69b", size = 155563, upload-time = "2026-01-31T23:25:39.17Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8c/4f54f7ea5addf208be44459393185aaa193bd2d0b8ecf4683b159fcc5238/wrapt-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:12687e6271df7ae5706bee44cc1f77fecb7805976ec9f14f58381b30ae2aceb5", size = 150189, upload-time = "2026-01-31T23:25:44.654Z" }, + { url = "https://files.pythonhosted.org/packages/b7/cc/e8290a1cd94297fbc1e9fbad06481b5a7c918f2db6645c550f05ee47f359/wrapt-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:38bbe336ee32f67eb99f886bd4f040d91310b7e660061bb03b9083d26e8cf915", size = 60431, upload-time = "2026-01-31T23:25:48.34Z" }, + { url = "https://files.pythonhosted.org/packages/d0/df/af5d244938853e3adb1251ca1397e9fa78d3e92adc808a0af0a8547585d3/wrapt-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0fa64a9a07df7f85b352adc42b43e7f44085fb11191b8f5b9b77219f7aaf7e17", size = 63859, upload-time = "2026-01-31T23:26:23.2Z" }, + { url = "https://files.pythonhosted.org/packages/39/c4/28b6f2804e8bc05d17114dfed03a80bce5b83ca2113fd44eecbef12275d1/wrapt-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:da379cbdf3b7d97ace33a69a391b7a7e2130b1aca94dc447246217994233974c", size = 60446, upload-time = "2026-01-31T23:25:41.001Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/70983b75d4abd6f85cffc6df79c623220ec5a579ceaacabac35c904b7b52/wrapt-2.1.0-py3-none-any.whl", hash = "sha256:e035693a0d25ea5bf5826df3e203dff7d091b0d5442aaefec9ca8f2bab38417f", size = 43886, upload-time = "2026-01-31T23:25:07.22Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +]