diff --git a/.github/workflows/indexability_tests.yml b/.github/workflows/indexability_tests.yml new file mode 100644 index 00000000..e703e675 --- /dev/null +++ b/.github/workflows/indexability_tests.yml @@ -0,0 +1,62 @@ +name: Docs Indexability Tests + +permissions: + contents: read + +on: + schedule: + # Run every Sunday at 22:00 UTC + - cron: "0 22 * * 0" + push: + branches: + - testing-ci + workflow_dispatch: + +env: + PYTHON_VERSION: "3.10" + +jobs: + test-indexability: + name: Test Indexability + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up test environment + uses: ./.github/actions/setup-test-env + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Record test start time + run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV + + - name: Run HTML structure tests + id: html-tests + continue-on-error: true + run: | + echo "Running HTML indexability tests..." + uv run pytest -m "indexability" -v --tb=short --junitxml=pytest-results.xml + + - name: Run agent tests + id: agent-tests + continue-on-error: true + if: env.ANTHROPIC_API_KEY != '' && env.OPENAI_API_KEY != '' + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + echo "Running agent indexability tests..." + uv run pytest -m "indexability_agents" -v --tb=short --junitxml=pytest-results-agents.xml + + - name: Handle test results + if: always() + uses: ./.github/actions/handle-test-results + with: + test-outcome: ${{ steps.html-tests.outcome }} + test-type: 'Indexability' + languages-tested: 'HTML + Claude + ChatGPT' + artifact-name: 'test-results-indexability' + slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }} diff --git a/pyproject.toml b/pyproject.toml index 8b4aaba6..0060c1e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,8 +5,11 @@ description = "Weaviate documentation test suite" readme = "README.md" requires-python = ">=3.10" dependencies = [ + "anthropic>=0.40.0", + "beautifulsoup4>=4.12.0", "datasets>=4.0.0", "ijson>=3.3.0", + "openai>=1.50.0", "pandas>=2.2.3", "pytest>=8.3.5", "python-dotenv>=1.1.1", diff --git a/pytest.ini b/pytest.ini index 49b6f1f5..e66a75bf 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,3 +7,5 @@ markers = ts: groups tests by language (typescript) java_v6: groups tests by language (java v6 client) csharp: groups tests by language (c#) + indexability: HTML structure tests for docs indexability (no API keys) + indexability_agents: AI agent tests for docs indexability (requires API keys) diff --git a/tests/README-INDEXABILITY.md b/tests/README-INDEXABILITY.md new file mode 100644 index 00000000..c53b52e9 --- /dev/null +++ b/tests/README-INDEXABILITY.md @@ -0,0 +1,132 @@ +# Docs Indexability Tests + +This test suite validates that AI agents (Claude, ChatGPT) and web search crawlers can access all documentation content from the live docs site at [docs.weaviate.io](https://docs.weaviate.io). + +## Why these tests exist + +Documentation content that is hidden behind JavaScript interactions (tabs, collapsibles) or lazy-loaded may be invisible to AI agents and search crawlers. These tests verify that: + +1. All HTML content is present in the server-rendered page (tabs, collapsibles, code snippets) +2. Claude can fetch and read page content via its `web_fetch` tool +3. ChatGPT can find and read page content via its `web_search_preview` tool +4. `llms.txt` and `sitemap.xml` are accessible and well-formed + +## Test structure + +| Part | Marker | API keys needed | Description | +|------|--------|-----------------|-------------| +| HTML structure | `indexability` | None | Fetches pages with `requests` + `beautifulsoup4` and checks HTML | +| Claude agent | `indexability_agents` | `ANTHROPIC_API_KEY` | Uses Anthropic API with `web_fetch` tool | +| ChatGPT agent | `indexability_agents` | `OPENAI_API_KEY` | Uses OpenAI Responses API with `web_search_preview` tool | + +### HTML structure tests (Part 1) + +These tests fetch ~11 representative pages from all doc sections and check: + +| Test | What it checks | +|------|---------------| +| `test_page_returns_200` | All pages return HTTP 200 | +| `test_meta_tags` | `
` blocks. For the quickstart page, also verifies the exact vectorizer config line for all 5 languages is present in the HTML. |
+| `test_details_content_present` | `` elements have body content (not lazy-loaded) |
+| `test_images_have_alt_text` | Content images have alt text (excludes decorative SVGs/icons) |
+| `test_llms_txt_accessible` | `/llms.txt` returns 200, has substantial content, mentions Weaviate |
+| `test_sitemap_accessible` | `/sitemap.xml` returns 200, has 100+ URLs |
+
+### Claude agent tests (Part 2)
+
+Uses Claude Haiku with the `web_fetch` tool:
+
+| Test | What it checks |
+|------|---------------|
+| `test_claude_can_fetch_code_tabs` | Fetches `/weaviate/quickstart` and extracts the exact vectorizer config line for all 5 languages (Python, TypeScript, Go, Java, C#) |
+| `test_claude_can_fetch_collapsible_content` | Fetches `/weaviate/config-refs/collections` and finds `text2vec-contextionary` inside a `` block |
+| `test_claude_can_fetch_llms_txt` | Fetches `/llms.txt` and identifies all 3 top-level sections (`agents`, `cloud`, `weaviate`) plus multi-language code examples |
+
+### ChatGPT agent tests (Part 3)
+
+Uses GPT-4.1 Mini with the `web_search_preview` tool:
+
+| Test | What it checks |
+|------|---------------|
+| `test_chatgpt_can_search_code_tabs` | Finds the quickstart URL, identifies 3+ languages, and checks for vectorizer config lines (requires 3/5) |
+| `test_chatgpt_can_search_collapsible_content` | Finds the config-refs URL and `text2vec-contextionary` from the collapsible JSON block |
+| `test_chatgpt_can_search_llms_txt` | Finds `/llms.txt` URL, identifies all 3 top-level sections (`agents`, `cloud`, `weaviate`), and multi-language code examples |
+
+## Running the tests
+
+```bash
+# HTML structure tests only (no API keys needed)
+uv run pytest -m indexability -v
+
+# Agent tests only (requires ANTHROPIC_API_KEY and OPENAI_API_KEY)
+uv run pytest -m indexability_agents -v
+
+# All indexability tests
+uv run pytest -m "indexability or indexability_agents" -v
+```
+
+## CI workflow
+
+The tests run via `.github/workflows/indexability_tests.yml`:
+
+- **Schedule**: Every Sunday at 22:00 UTC
+- **Manual**: Via workflow dispatch
+- **Branch**: Runs on push to `testing-ci`
+- **Runtime**: ~15 minutes maximum
+
+HTML structure tests always run. Agent tests only run if `ANTHROPIC_API_KEY` and `OPENAI_API_KEY` secrets are configured.
+
+## Test pages
+
+The suite tests 11 representative URLs covering all doc sections:
+
+| Page | Features tested |
+|------|----------------|
+| `/weaviate/quickstart` | tabs, code (with vectorizer line check) |
+| `/weaviate/manage-collections/collection-operations` | tabs, code, details |
+| `/weaviate/search/similarity` | tabs, code |
+| `/weaviate/search/hybrid` | tabs, code |
+| `/weaviate/connections/connect-cloud` | tabs, code |
+| `/weaviate/config-refs/collections` | details, table |
+| `/weaviate/concepts/data-import` | images |
+| `/cloud/quickstart` | code, images |
+| `/cloud/manage-clusters/create` | images |
+| `/agents/query/tutorial-ecommerce` | code |
+| `/weaviate/search` | landing page |
+
+## Quickstart vectorizer lines
+
+The quickstart page has tabbed code for 5 languages. The tests verify these exact lines are present in the HTML and readable by Claude:
+
+| Language | Vectorizer config line |
+|----------|----------------------|
+| Python | `Configure.Vectors.text2vec_weaviate()` |
+| TypeScript | `vectors.text2VecWeaviate()` |
+| Go | `Vectorizer: "text2vec-weaviate"` |
+| Java | `VectorConfig.text2vecWeaviate()` |
+| C# | `v.Text2VecWeaviate()` |
+
+## Dependencies
+
+- `beautifulsoup4` — HTML parsing
+- `requests` — HTTP fetching (already in project)
+- `anthropic` — Claude API for agent tests
+- `openai` — OpenAI API for agent tests
+
+All are listed in the root `pyproject.toml`.
+
+## Adding test pages
+
+To test additional pages, add entries to the `TEST_PAGES` list in `tests/test_docs_indexability.py`:
+
+```python
+TEST_PAGES = [
+ ("/path/to/page", {"tabs", "code", "details", "images", "table"}),
+ # ...
+]
+```
+
+Available feature tags: `tabs`, `code`, `details`, `images`, `table`. Pages are parametrized — each feature tag enables the corresponding structural test for that page.
diff --git a/tests/test_docs_indexability.py b/tests/test_docs_indexability.py
new file mode 100644
index 00000000..a9249486
--- /dev/null
+++ b/tests/test_docs_indexability.py
@@ -0,0 +1,630 @@
+"""
+Docs Indexability Test Suite
+
+Validates that AI agents and web crawlers can access all documentation content
+from the live docs site. Tests cover:
+1. HTML structure (tabs, collapsibles, code snippets visible in source)
+2. Claude web_fetch tool access
+3. ChatGPT web_search_preview tool access
+4. llms.txt and sitemap.xml accessibility
+"""
+
+import os
+import time
+from functools import lru_cache
+
+import pytest
+import requests
+from bs4 import BeautifulSoup
+
+# ---------------------------------------------------------------------------
+# Constants
+# ---------------------------------------------------------------------------
+
+BASE_URL = "https://docs.weaviate.io"
+
+# Representative pages with expected features
+TEST_PAGES = [
+ ("/weaviate/quickstart", {"tabs", "code"}),
+ ("/weaviate/manage-collections/collection-operations", {"tabs", "code", "details"}),
+ ("/weaviate/search/similarity", {"tabs", "code"}),
+ ("/weaviate/search/hybrid", {"tabs", "code"}),
+ ("/weaviate/connections/connect-cloud", {"tabs", "code"}),
+ ("/weaviate/config-refs/collections", {"details", "table"}),
+ ("/weaviate/concepts/data-import", {"images"}),
+ ("/cloud/quickstart", {"code", "images"}),
+ ("/cloud/manage-clusters/create", {"images"}),
+ ("/agents/query/tutorial-ecommerce", {"code"}),
+ ("/weaviate/search", set()), # landing page
+]
+
+ALL_PATHS = [path for path, _ in TEST_PAGES]
+
+# ---------------------------------------------------------------------------
+# Fixtures
+# ---------------------------------------------------------------------------
+
+_page_cache: dict[str, requests.Response] = {}
+_last_fetch_time: float = 0
+
+
+def _fetch_page(path: str) -> requests.Response:
+ """Fetch a page with caching and 1-second rate limiting."""
+ global _last_fetch_time
+
+ if path in _page_cache:
+ return _page_cache[path]
+
+ elapsed = time.time() - _last_fetch_time
+ if elapsed < 1:
+ time.sleep(1 - elapsed)
+
+ url = f"{BASE_URL}{path}"
+ resp = requests.get(url, timeout=30, headers={"User-Agent": "WeaviateDocsIndexabilityTest/1.0"})
+ _last_fetch_time = time.time()
+ _page_cache[path] = resp
+ return resp
+
+
+def _get_soup(path: str) -> BeautifulSoup:
+ resp = _fetch_page(path)
+ return BeautifulSoup(resp.text, "html.parser")
+
+
+def _features_for(path: str) -> set[str]:
+ for p, features in TEST_PAGES:
+ if p == path:
+ return features
+ return set()
+
+
+# ---------------------------------------------------------------------------
+# Part 1: HTML Structure Tests (no API keys needed)
+# ---------------------------------------------------------------------------
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize("path", ALL_PATHS)
+def test_page_returns_200(path):
+ """All doc pages return HTTP 200."""
+ resp = _fetch_page(path)
+ assert resp.status_code == 200, f"{path} returned {resp.status_code}"
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize("path", ALL_PATHS)
+def test_meta_tags(path):
+ """Pages have essential meta tags for SEO and social sharing."""
+ soup = _get_soup(path)
+
+ title = soup.find("title")
+ assert title and title.string and len(title.string.strip()) > 0, f"{path}: missing "
+
+ desc = soup.find("meta", attrs={"name": "description"})
+ assert desc and desc.get("content"), f"{path}: missing meta description"
+
+ og_title = soup.find("meta", attrs={"property": "og:title"})
+ assert og_title and og_title.get("content"), f"{path}: missing og:title"
+
+ og_desc = soup.find("meta", attrs={"property": "og:description"})
+ assert og_desc and og_desc.get("content"), f"{path}: missing og:description"
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize("path", ALL_PATHS)
+def test_heading_hierarchy(path):
+ """Pages have exactly 1 h1 and content pages have h2s."""
+ soup = _get_soup(path)
+
+ h1s = soup.find_all("h1")
+ assert len(h1s) == 1, f"{path}: expected 1 h1, found {len(h1s)}"
+
+ # Content pages (not landing pages) should have h2s
+ features = _features_for(path)
+ if features: # non-empty features means it's a content page
+ h2s = soup.find_all("h2")
+ assert len(h2s) > 0, f"{path}: content page has no h2 headings"
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize(
+ "path",
+ [p for p, f in TEST_PAGES if "tabs" in f],
+ ids=[p for p, f in TEST_PAGES if "tabs" in f],
+)
+def test_tabbed_code_blocks_all_present(path):
+ """ALL tab panels have content in HTML (not just the active tab)."""
+ soup = _get_soup(path)
+
+ # Docusaurus tabs use role="tabpanel" inside a tabs container
+ tab_panels = soup.find_all(attrs={"role": "tabpanel"})
+ assert len(tab_panels) > 0, f"{path}: no tab panels found"
+
+ # Check that tab panels have content (even hidden ones should be in DOM)
+ # Docusaurus renders all tab content in HTML but hides inactive with CSS
+ panels_with_content = [
+ panel for panel in tab_panels
+ if panel.get_text(strip=True)
+ ]
+ assert len(panels_with_content) >= 2, (
+ f"{path}: expected multiple tab panels with content, "
+ f"found {len(panels_with_content)} of {len(tab_panels)}"
+ )
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize(
+ "path",
+ [p for p, f in TEST_PAGES if "code" in f],
+ ids=[p for p, f in TEST_PAGES if "code" in f],
+)
+def test_code_blocks_present(path):
+ """Pages with code feature have non-empty blocks."""
+ soup = _get_soup(path)
+
+ code_blocks = soup.find_all("pre")
+ assert len(code_blocks) > 0, f"{path}: no code blocks found"
+
+ non_empty = [
+ block for block in code_blocks
+ if block.get_text(strip=True)
+ ]
+ assert len(non_empty) > 0, f"{path}: all code blocks are empty"
+
+ # For the quickstart page, verify the actual vectorizer config lines
+ # from all 5 language tabs are present in the rendered HTML code blocks.
+ if path == "/weaviate/quickstart":
+ all_code_text = "\n".join(block.get_text() for block in code_blocks)
+ missing = [
+ lang for lang, line in QUICKSTART_VECTORIZER_LINES.items()
+ if line not in all_code_text
+ ]
+ print(all_code_text)
+ assert len(missing) == 0, (
+ f"Quickstart HTML missing vectorizer config for: {', '.join(missing)}"
+ )
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize(
+ "path",
+ [p for p, f in TEST_PAGES if "details" in f],
+ ids=[p for p, f in TEST_PAGES if "details" in f],
+)
+def test_details_content_present(path):
+ """ elements have visible content in HTML (not lazy-loaded)."""
+ soup = _get_soup(path)
+
+ details = soup.find_all("details")
+ assert len(details) > 0, f"{path}: no elements found"
+
+ for i, detail in enumerate(details):
+ content = detail.get_text(strip=True)
+ # Subtract the summary text to check the body
+ summary = detail.find("summary")
+ summary_text = summary.get_text(strip=True) if summary else ""
+ body_text = content.replace(summary_text, "", 1).strip()
+ assert len(body_text) > 0, f"{path}: #{i} has no body content"
+
+
+@pytest.mark.indexability
+@pytest.mark.parametrize(
+ "path",
+ [p for p, f in TEST_PAGES if "images" in f],
+ ids=[p for p, f in TEST_PAGES if "images" in f],
+)
+def test_images_have_alt_text(path):
+ """Content images have alt text (excludes SVG icons and badges)."""
+ soup = _get_soup(path)
+
+ # Find content images, excluding decorative ones
+ images = soup.find_all("img")
+ content_images = [
+ img for img in images
+ if not _is_decorative_image(img)
+ ]
+
+ assert len(content_images) > 0, f"{path}: no content images found"
+
+ missing_alt = [
+ img.get("src", "unknown")
+ for img in content_images
+ if not img.get("alt")
+ ]
+ assert len(missing_alt) == 0, (
+ f"{path}: {len(missing_alt)} images missing alt text: {missing_alt[:5]}"
+ )
+
+
+def _is_decorative_image(img) -> bool:
+ """Check if an image is decorative (SVG icon, badge, etc.)."""
+ src = img.get("src", "")
+ classes = img.get("class", [])
+
+ # Skip SVG data URIs, badge images, tiny icons
+ if src.startswith("data:image/svg"):
+ return True
+ if "badge" in src.lower() or "shield" in src.lower():
+ return True
+ if any(c in ("icon", "badge", "logo") for c in classes):
+ return True
+ # Skip language/site logo SVGs (e.g., /img/site/logo-py.svg)
+ if src.endswith(".svg") and "/img/site/" in src:
+ return True
+ # Skip very small images (likely icons)
+ width = img.get("width")
+ if width and str(width).isdigit() and int(width) < 30:
+ return True
+
+ return False
+
+
+@pytest.mark.indexability
+def test_llms_txt_accessible():
+ """/llms.txt returns 200, has substantial content, and mentions Weaviate."""
+ resp = requests.get(
+ f"{BASE_URL}/llms.txt",
+ timeout=30,
+ headers={"User-Agent": "WeaviateDocsIndexabilityTest/1.0"},
+ )
+ assert resp.status_code == 200, f"/llms.txt returned {resp.status_code}"
+ assert len(resp.text) > 500, f"/llms.txt content too short ({len(resp.text)} chars)"
+ assert "weaviate" in resp.text.lower(), "/llms.txt doesn't mention Weaviate"
+
+
+@pytest.mark.indexability
+def test_sitemap_accessible():
+ """/sitemap.xml returns 200 and has 100+ URLs."""
+ resp = requests.get(
+ f"{BASE_URL}/sitemap.xml",
+ timeout=30,
+ headers={"User-Agent": "WeaviateDocsIndexabilityTest/1.0"},
+ )
+ assert resp.status_code == 200, f"/sitemap.xml returned {resp.status_code}"
+
+ # Count entries (use html.parser — lxml may not be installed)
+ import warnings
+ from bs4 import XMLParsedAsHTMLWarning
+ warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
+ soup = BeautifulSoup(resp.text, "html.parser")
+ locs = soup.find_all("loc")
+ assert len(locs) >= 100, f"/sitemap.xml has only {len(locs)} URLs (expected 100+)"
+
+
+# ---------------------------------------------------------------------------
+# Part 2: Claude Agent Test (requires ANTHROPIC_API_KEY)
+# ---------------------------------------------------------------------------
+
+
+def _extract_text_from_response(response) -> str:
+ """Extract all text content from an Anthropic API response."""
+ texts = []
+ for block in response.content:
+ if hasattr(block, "text"):
+ texts.append(block.text)
+ return "\n".join(texts)
+
+
+# Exact vectorizer configuration lines from the quickstart page.
+# Each language tab has a distinctive line that configures the vectorizer.
+# These are the verbatim lines from the source code files.
+QUICKSTART_VECTORIZER_LINES = {
+ "Python": "Configure.Vectors.text2vec_weaviate()",
+ "TypeScript": "vectors.text2VecWeaviate()",
+ "Go": 'Vectorizer: "text2vec-weaviate"',
+ "Java": "VectorConfig.text2vecWeaviate()",
+ "C#": "v.Text2VecWeaviate()",
+}
+
+
+@pytest.mark.indexability_agents
+def test_claude_can_fetch_code_tabs():
+ """Claude's web_fetch tool can retrieve quickstart vectorizer config for all languages."""
+ anthropic = pytest.importorskip("anthropic")
+
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
+ if not api_key:
+ pytest.skip("ANTHROPIC_API_KEY not set")
+
+ client = anthropic.Anthropic()
+ url = f"{BASE_URL}/weaviate/quickstart"
+
+ response = client.messages.create(
+ model="claude-haiku-4-5-20251001",
+ max_tokens=4096,
+ tools=[{
+ "type": "web_fetch_20250910",
+ "name": "web_fetch",
+ "max_uses": 1,
+ "allowed_domains": ["docs.weaviate.io"],
+ }],
+ messages=[{
+ "role": "user",
+ "content": (
+ f"Fetch {url} and find the code that configures the vectorizer "
+ "in the collection creation step. The page has tabs for Python, "
+ "TypeScript, Go, Java, and C#. For EACH language, copy the exact "
+ "line that sets up the vectorizer (e.g. text2vec_weaviate, "
+ "text2VecWeaviate, etc.) verbatim from the code snippet. "
+ "Format your response as:\n"
+ "Python: \n"
+ "TypeScript: \n"
+ "Go: \n"
+ "Java: \n"
+ "C#: "
+ ),
+ }],
+ )
+
+ text = _extract_text_from_response(response)
+
+ missing = []
+ for lang, line in QUICKSTART_VECTORIZER_LINES.items():
+ if line not in text:
+ missing.append(lang)
+
+ assert len(missing) == 0, (
+ f"Claude couldn't extract vectorizer config for: {', '.join(missing)}. "
+ f"Response:\n{text[:2000]}"
+ )
+
+
+@pytest.mark.indexability_agents
+def test_claude_can_fetch_collapsible_content():
+ """Claude's web_fetch tool can read content inside collapsible sections."""
+ anthropic = pytest.importorskip("anthropic")
+
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
+ if not api_key:
+ pytest.skip("ANTHROPIC_API_KEY not set")
+
+ client = anthropic.Anthropic()
+ url = f"{BASE_URL}/weaviate/config-refs/collections"
+
+ # The page has a collapsible section containing a full JSON config example
+ # that includes this line (inside a block):
+ # "vectorizer": "text2vec-contextionary", // Vectorizer to use ...
+ # Ask Claude to find and quote it to prove collapsible content is readable.
+ response = client.messages.create(
+ model="claude-haiku-4-5-20251001",
+ max_tokens=2048,
+ tools=[{
+ "type": "web_fetch_20250910",
+ "name": "web_fetch",
+ "max_uses": 1,
+ "allowed_domains": ["docs.weaviate.io"],
+ }],
+ messages=[{
+ "role": "user",
+ "content": (
+ f"Fetch {url} and look for an expandable/collapsible section "
+ "that contains a full JSON configuration example. "
+ "Find the line that sets the vectorizer to 'text2vec-contextionary' "
+ "and copy it verbatim. Also list 2-3 other configuration fields "
+ "from that same JSON block."
+ ),
+ }],
+ )
+
+ text = _extract_text_from_response(response)
+ assert "text2vec-contextionary" in text, (
+ f"Claude couldn't find 'text2vec-contextionary' in collapsible content. "
+ f"Response:\n{text[:1000]}"
+ )
+
+
+@pytest.mark.indexability_agents
+def test_claude_can_fetch_llms_txt():
+ """Claude's web_fetch tool can read /llms.txt."""
+ anthropic = pytest.importorskip("anthropic")
+
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
+ if not api_key:
+ pytest.skip("ANTHROPIC_API_KEY not set")
+
+ client = anthropic.Anthropic()
+ url = f"{BASE_URL}/llms.txt"
+
+ # The llms.txt file starts with "# Weaviate Documentation" and contains
+ # section headings like "## agents", "## cloud", "## weaviate".
+ # Ask Claude to quote specific content to prove it fetched the real file.
+ response = client.messages.create(
+ model="claude-haiku-4-5-20251001",
+ max_tokens=2048,
+ tools=[{
+ "type": "web_fetch_20250910",
+ "name": "web_fetch",
+ "max_uses": 1,
+ "allowed_domains": ["docs.weaviate.io"],
+ }],
+ messages=[{
+ "role": "user",
+ "content": (
+ f"Fetch {url} and tell me: "
+ "1) What is the first heading line of the file (copy it verbatim)? "
+ "2) List ALL the top-level section headings (lines starting with '## '). "
+ "3) Does it mention code examples in multiple languages? Which ones?"
+ ),
+ }],
+ )
+
+ text = _extract_text_from_response(response)
+ text_lower = text.lower()
+
+ # Must identify Weaviate
+ assert "weaviate" in text_lower, (
+ f"Claude couldn't identify Weaviate in llms.txt. Response: {text[:500]}"
+ )
+
+ # Must find the key top-level sections from llms.txt
+ for section in ["agents", "cloud", "weaviate"]:
+ assert section in text_lower, (
+ f"Claude didn't find '{section}' section in llms.txt. Response: {text[:1000]}"
+ )
+
+ # Must identify multi-language code examples
+ assert "python" in text_lower, (
+ f"Claude didn't find Python mentioned in llms.txt. Response: {text[:500]}"
+ )
+
+
+# ---------------------------------------------------------------------------
+# Part 3: ChatGPT Agent Test (requires OPENAI_API_KEY)
+# ---------------------------------------------------------------------------
+
+
+@pytest.mark.indexability_agents
+def test_chatgpt_can_search_code_tabs():
+ """ChatGPT's web_search can find the quickstart and identify vectorizer config."""
+ openai = pytest.importorskip("openai")
+
+ api_key = os.environ.get("OPENAI_API_KEY")
+ if not api_key:
+ pytest.skip("OPENAI_API_KEY not set")
+
+ client = openai.OpenAI()
+
+ # web_search_preview searches the web but can't guarantee extracting
+ # verbatim code. We verify ChatGPT finds the correct URL and identifies
+ # the vectorizer configuration from the code tabs.
+ response = client.responses.create(
+ model="gpt-4.1-mini",
+ tools=[{
+ "type": "web_search_preview",
+ "search_context_size": "high",
+ }],
+ input=(
+ "Search for the Weaviate quickstart guide at docs.weaviate.io. "
+ "The page creates a 'Movie' collection and has code tabs for "
+ "Python, TypeScript, Go, Java, and C#. "
+ "Tell me: 1) The exact URL you found "
+ "2) What programming languages have code examples "
+ "3) For each language, what is the exact vectorizer configuration "
+ "line from the code in the quickstart (e.g. text2vec_weaviate, text2VecWeaviate, etc.)"
+ ),
+ )
+
+ text = response.output_text
+ text_lower = text.lower()
+
+ # Must find the correct quickstart URL
+ assert "docs.weaviate.io/weaviate/quickstart" in text, (
+ f"ChatGPT didn't find the quickstart URL. Response:\n{text[:1000]}"
+ )
+
+ # Must identify multiple programming languages from the code tabs
+ langs_found = sum(
+ 1 for lang in ["python", "typescript", "go", "java", "c#"]
+ if lang in text_lower
+ )
+ assert langs_found >= 3, (
+ f"ChatGPT only found {langs_found} languages (expected 3+). "
+ f"Response:\n{text[:1000]}"
+ )
+
+ # Must find at least 3 of the 5 exact vectorizer config lines.
+ # web_search_preview may not extract all tabs verbatim, but should
+ # get most of them from the indexed page content.
+ vectorizer_found = sum(
+ 1 for line in QUICKSTART_VECTORIZER_LINES.values()
+ if line in text
+ )
+ assert vectorizer_found >= 3, (
+ f"ChatGPT only found {vectorizer_found}/5 vectorizer lines (expected 3+). "
+ f"Response:\n{text[:2000]}"
+ )
+
+
+@pytest.mark.indexability_agents
+def test_chatgpt_can_search_collapsible_content():
+ """ChatGPT's web_search can find content inside collapsible sections."""
+ openai = pytest.importorskip("openai")
+
+ api_key = os.environ.get("OPENAI_API_KEY")
+ if not api_key:
+ pytest.skip("OPENAI_API_KEY not set")
+
+ client = openai.OpenAI()
+
+ response = client.responses.create(
+ model="gpt-4.1-mini",
+ tools=[{
+ "type": "web_search_preview",
+ "search_context_size": "high",
+ }],
+ input=(
+ "Search for the Weaviate collection configuration reference page at "
+ "docs.weaviate.io/weaviate/config-refs/collections. "
+ "The page has an expandable/collapsible section with a full JSON "
+ "configuration example that includes a vectorizer setting. "
+ "Tell me: 1) The exact URL you found "
+ "2) What vectorizer is configured in the JSON example "
+ "3) Copy the exact line that sets the vectorizer"
+ ),
+ )
+
+ text = response.output_text
+
+ # Must find the correct URL
+ assert "docs.weaviate.io/weaviate/config-refs/collections" in text, (
+ f"ChatGPT didn't find the config-refs URL. Response:\n{text[:1000]}"
+ )
+
+ # Must find text2vec-contextionary from the collapsible JSON block
+ assert "text2vec-contextionary" in text, (
+ f"ChatGPT couldn't find 'text2vec-contextionary' in collapsible content. "
+ f"Response:\n{text[:1000]}"
+ )
+
+
+@pytest.mark.indexability_agents
+def test_chatgpt_can_search_llms_txt():
+ """ChatGPT's web_search can find and read /llms.txt."""
+ openai = pytest.importorskip("openai")
+
+ api_key = os.environ.get("OPENAI_API_KEY")
+ if not api_key:
+ pytest.skip("OPENAI_API_KEY not set")
+
+ client = openai.OpenAI()
+
+ response = client.responses.create(
+ model="gpt-4.1-mini",
+ tools=[{
+ "type": "web_search_preview",
+ "search_context_size": "high",
+ }],
+ input=(
+ "Search for the llms.txt file at docs.weaviate.io/llms.txt. "
+ "This is a special file that describes documentation for LLMs. "
+ "Tell me: 1) The exact URL you found "
+ "2) What is the first heading line of the file "
+ "3) List ALL the top-level section headings (lines starting with '## ') "
+ "4) Does it mention code examples in multiple languages? Which ones?"
+ ),
+ )
+
+ text = response.output_text
+ text_lower = text.lower()
+
+ # Must find the correct URL
+ assert "docs.weaviate.io/llms.txt" in text, (
+ f"ChatGPT didn't find the llms.txt URL. Response:\n{text[:1000]}"
+ )
+
+ # Must identify Weaviate
+ assert "weaviate" in text_lower, (
+ f"ChatGPT couldn't identify Weaviate in llms.txt. Response:\n{text[:500]}"
+ )
+
+ # Must find the key top-level sections from llms.txt
+ for section in ["agents", "cloud", "weaviate"]:
+ assert section in text_lower, (
+ f"ChatGPT didn't find '{section}' section in llms.txt. "
+ f"Response:\n{text[:1000]}"
+ )
+
+ # Must identify multi-language code examples
+ assert "python" in text_lower, (
+ f"ChatGPT didn't find Python mentioned in llms.txt. Response:\n{text[:500]}"
+ )
diff --git a/tools/chatgpt_fetch_quickstart.py b/tools/chatgpt_fetch_quickstart.py
new file mode 100644
index 00000000..123af7d9
--- /dev/null
+++ b/tools/chatgpt_fetch_quickstart.py
@@ -0,0 +1,32 @@
+"""Fetch the Weaviate quickstart page via ChatGPT's web_search_preview and print everything."""
+
+import json
+import openai
+
+client = openai.OpenAI()
+
+response = client.responses.create(
+ model="gpt-4.1-mini",
+ tools=[{
+ "type": "web_search_preview",
+ "search_context_size": "high",
+ }],
+ input=(
+ "Go to https://docs.weaviate.io/weaviate/quickstart and read the full page. "
+ "Print EVERYTHING you can see on the page: all text, all code snippets from "
+ "every language tab (Python, TypeScript, Go, Java, C#), all headings, all steps. "
+ "Do not summarize — reproduce the full page content as faithfully as possible."
+ ),
+)
+
+print("=" * 80)
+print("OUTPUT TEXT:")
+print("=" * 80)
+print(response.output_text)
+print()
+print("=" * 80)
+print("FULL RESPONSE OUTPUT (raw):")
+print("=" * 80)
+for item in response.output:
+ print(f"\n--- {item.type} ---")
+ print(json.dumps(item.to_dict(), indent=2, default=str))
diff --git a/uv.lock b/uv.lock
index ce81d4e9..159dbf36 100644
--- a/uv.lock
+++ b/uv.lock
@@ -167,6 +167,25 @@ 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.84.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/ea/0869d6df9ef83dcf393aeefc12dd81677d091c6ffc86f783e51cf44062f2/anthropic-0.84.0.tar.gz", hash = "sha256:72f5f90e5aebe62dca316cb013629cfa24996b0f5a4593b8c3d712bc03c43c37", size = 539457, upload-time = "2026-02-25T05:22:38.54Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/64/ca/218fa25002a332c0aa149ba18ffc0543175998b1f65de63f6d106689a345/anthropic-0.84.0-py3-none-any.whl", hash = "sha256:861c4c50f91ca45f942e091d83b60530ad6d4f98733bfe648065364da05d29e7", size = 455156, upload-time = "2026-02-25T05:22:40.468Z" },
+]
+
[[package]]
name = "anyio"
version = "4.9.0"
@@ -212,6 +231,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/73/f7084bf12755113cd535ae586782ff3a6e710bfbe6a0d13d1c2f81ffbbfa/authlib-1.6.8-py2.py3-none-any.whl", hash = "sha256:97286fd7a15e6cfefc32771c8ef9c54f0ed58028f1322de6a2a7c969c3817888", size = 244116, upload-time = "2026-02-14T04:02:15.579Z" },
]
+[[package]]
+name = "beautifulsoup4"
+version = "4.14.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "soupsieve" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
+]
+
[[package]]
name = "certifi"
version = "2025.7.14"
@@ -454,6 +486,24 @@ 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 = "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 = "exceptiongroup"
version = "1.3.0"
@@ -847,6 +897,103 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
]
+[[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/d0/5a/41da76c5ea07bec1b0472b6b2fdb1b651074d504b19374d7e130e0cdfb25/jiter-0.13.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2ffc63785fd6c7977defe49b9824ae6ce2b2e2b77ce539bdaf006c26da06342e", size = 311164, upload-time = "2026-02-02T12:35:17.688Z" },
+ { url = "https://files.pythonhosted.org/packages/40/cb/4a1bf994a3e869f0d39d10e11efb471b76d0ad70ecbfb591427a46c880c2/jiter-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a638816427006c1e3f0013eb66d391d7a3acda99a7b0cf091eff4497ccea33a", size = 320296, upload-time = "2026-02-02T12:35:19.828Z" },
+ { url = "https://files.pythonhosted.org/packages/09/82/acd71ca9b50ecebadc3979c541cd717cce2fe2bc86236f4fa597565d8f1a/jiter-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19928b5d1ce0ff8c1ee1b9bdef3b5bfc19e8304f1b904e436caf30bc15dc6cf5", size = 352742, upload-time = "2026-02-02T12:35:21.258Z" },
+ { url = "https://files.pythonhosted.org/packages/71/03/d1fc996f3aecfd42eb70922edecfb6dd26421c874503e241153ad41df94f/jiter-0.13.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:309549b778b949d731a2f0e1594a3f805716be704a73bf3ad9a807eed5eb5721", size = 363145, upload-time = "2026-02-02T12:35:24.653Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/61/a30492366378cc7a93088858f8991acd7d959759fe6138c12a4644e58e81/jiter-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcdabaea26cb04e25df3103ce47f97466627999260290349a88c8136ecae0060", size = 487683, upload-time = "2026-02-02T12:35:26.162Z" },
+ { url = "https://files.pythonhosted.org/packages/20/4e/4223cffa9dbbbc96ed821c5aeb6bca510848c72c02086d1ed3f1da3d58a7/jiter-0.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a377af27b236abbf665a69b2bdd680e3b5a0bd2af825cd3b81245279a7606c", size = 373579, upload-time = "2026-02-02T12:35:27.582Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/c9/b0489a01329ab07a83812d9ebcffe7820a38163c6d9e7da644f926ff877c/jiter-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe49d3ff6db74321f144dff9addd4a5874d3105ac5ba7c5b77fac099cfae31ae", size = 362904, upload-time = "2026-02-02T12:35:28.925Z" },
+ { url = "https://files.pythonhosted.org/packages/05/af/53e561352a44afcba9a9bc67ee1d320b05a370aed8df54eafe714c4e454d/jiter-0.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2113c17c9a67071b0f820733c0893ed1d467b5fcf4414068169e5c2cabddb1e2", size = 392380, upload-time = "2026-02-02T12:35:30.385Z" },
+ { url = "https://files.pythonhosted.org/packages/76/2a/dd805c3afb8ed5b326c5ae49e725d1b1255b9754b1b77dbecdc621b20773/jiter-0.13.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ab1185ca5c8b9491b55ebf6c1e8866b8f68258612899693e24a92c5fdb9455d5", size = 517939, upload-time = "2026-02-02T12:35:31.865Z" },
+ { url = "https://files.pythonhosted.org/packages/20/2a/7b67d76f55b8fe14c937e7640389612f05f9a4145fc28ae128aaa5e62257/jiter-0.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9621ca242547edc16400981ca3231e0c91c0c4c1ab8573a596cd9bb3575d5c2b", size = 551696, upload-time = "2026-02-02T12:35:33.306Z" },
+ { url = "https://files.pythonhosted.org/packages/85/9c/57cdd64dac8f4c6ab8f994fe0eb04dc9fd1db102856a4458fcf8a99dfa62/jiter-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a7637d92b1c9d7a771e8c56f445c7f84396d48f2e756e5978840ecba2fac0894", size = 204592, upload-time = "2026-02-02T12:35:34.58Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/38/f4f3ea5788b8a5bae7510a678cdc747eda0c45ffe534f9878ff37e7cf3b3/jiter-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c1b609e5cbd2f52bb74fb721515745b407df26d7b800458bd97cb3b972c29e7d", size = 206016, upload-time = "2026-02-02T12:35:36.435Z" },
+ { 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 = "3.0.0"
@@ -1179,6 +1326,25 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/78/e3/6690b3f85a05506733c7e90b577e4762517404ea78bab2ca3a5cb1aeb78d/numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619", size = 12977811, upload-time = "2025-07-24T21:29:18.234Z" },
]
+[[package]]
+name = "openai"
+version = "2.26.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/d7/91/2a06c4e9597c338cac1e5e5a8dd6f29e1836fc229c4c523529dca387fda8/openai-2.26.0.tar.gz", hash = "sha256:b41f37c140ae0034a6e92b0c509376d907f3a66109935fba2c1b471a7c05a8fb", size = 666702, upload-time = "2026-03-05T23:17:35.874Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/2e/3f73e8ca53718952222cacd0cf7eecc9db439d020f0c1fe7ae717e4e199a/openai-2.26.0-py3-none-any.whl", hash = "sha256:6151bf8f83802f036117f06cc8a57b3a4da60da9926826cc96747888b57f394f", size = 1136409, upload-time = "2026-03-05T23:17:34.072Z" },
+]
+
[[package]]
name = "packaging"
version = "25.0"
@@ -1749,6 +1915,15 @@ 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 = "soupsieve"
+version = "2.8.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" },
+]
+
[[package]]
name = "tomli"
version = "2.2.1"
@@ -1927,8 +2102,11 @@ name = "weaviate-docs"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
+ { name = "anthropic" },
+ { name = "beautifulsoup4" },
{ name = "datasets" },
{ name = "ijson" },
+ { name = "openai" },
{ name = "pandas" },
{ name = "pytest" },
{ name = "python-dotenv" },
@@ -1941,8 +2119,11 @@ dependencies = [
[package.metadata]
requires-dist = [
+ { name = "anthropic", specifier = ">=0.40.0" },
+ { name = "beautifulsoup4", specifier = ">=4.12.0" },
{ name = "datasets", specifier = ">=4.0.0" },
{ name = "ijson", specifier = ">=3.3.0" },
+ { name = "openai", specifier = ">=1.50.0" },
{ name = "pandas", specifier = ">=2.2.3" },
{ name = "pytest", specifier = ">=8.3.5" },
{ name = "python-dotenv", specifier = ">=1.1.1" },