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` | ``, `<meta description>`, `og:title`, `og:description` present | +| `test_heading_hierarchy` | Exactly 1 `<h1>`, content pages have `<h2>`s | +| `test_tabbed_code_blocks_all_present` | ALL tab panels have content in HTML (not just active tab) | +| `test_code_blocks_present` | Pages with code have non-empty `<pre><code>` 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` | `<details>` 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 `<details>` 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 <title>" + + 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 <pre><code> blocks.""" + soup = _get_soup(path) + + code_blocks = soup.find_all("pre") + assert len(code_blocks) > 0, f"{path}: no <pre> 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): + """<details> 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 <details> 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}: <details> #{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 <loc> 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: <exact vectorizer config line>\n" + "TypeScript: <exact vectorizer config line>\n" + "Go: <exact vectorizer config line>\n" + "Java: <exact vectorizer config line>\n" + "C#: <exact vectorizer config line>" + ), + }], + ) + + 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 <details> 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" },