diff --git a/.github/workflows/test-snippets.yml b/.github/workflows/test-snippets.yml index 873c456..1db25f9 100644 --- a/.github/workflows/test-snippets.yml +++ b/.github/workflows/test-snippets.yml @@ -6,7 +6,10 @@ on: paths: - 'v3/**/*.mdx' - '*.mdx' + - 'docs.json' - 'tests/**' + schedule: + - cron: '0 6 * * 1' # These tests create shared, named resources on one Eden AI account (custom # tokens, uploaded files), so two runs of the same ref must not overlap. @@ -24,23 +27,31 @@ concurrency: cancel-in-progress: false jobs: - execute: - name: Execution Tests + python-tests: + name: Python Tests runs-on: ubuntu-latest + env: + EDEN_AI_BASE_URL: ${{ vars.EDEN_AI_BASE_URL || 'https://staging-api.edenai.run' }} + EDEN_AI_SANDBOX_API_TOKEN: ${{ secrets.EDEN_AI_SANDBOX_TOKEN }} + EDEN_AI_PRODUCTION_API_TOKEN: ${{ secrets.EDEN_AI_PRODUCTION_TOKEN }} steps: - uses: actions/checkout@v4 - uses: astral-sh/setup-uv@v5 - uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install dependencies run: uv pip install -r tests/requirements-lock.txt --system - - name: Run tests + - name: Run snippet execution tests run: pytest tests/test_snippets_execute.py - env: - EDEN_AI_BASE_URL: ${{ vars.EDEN_AI_BASE_URL || 'https://staging-api.edenai.run' }} - EDEN_AI_SANDBOX_API_TOKEN: ${{ secrets.EDEN_AI_SANDBOX_TOKEN }} - EDEN_AI_PRODUCTION_API_TOKEN: ${{ secrets.EDEN_AI_PRODUCTION_TOKEN }} + - name: Run validators + if: always() + run: | + pytest \ + tests/config_validator.py \ + tests/api_reference_validator.py \ + tests/model_provider_validator.py \ + tests/link_checker.py - name: Coverage summary if: always() run: | @@ -50,3 +61,26 @@ jobs: coverage report -m >> "$GITHUB_STEP_SUMMARY" echo '```' >> "$GITHUB_STEP_SUMMARY" + ts-tests: + name: TypeScript Tests + runs-on: ubuntu-latest + env: + EDEN_AI_BASE_URL: ${{ vars.EDEN_AI_BASE_URL || 'https://staging-api.edenai.run' }} + EDEN_AI_SANDBOX_API_TOKEN: ${{ secrets.EDEN_AI_SANDBOX_TOKEN }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - uses: astral-sh/setup-uv@v5 + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install Python deps (needed for the snippet extractor) + run: uv pip install -r tests/requirements-lock.txt --system + - name: Install TS deps + working-directory: tests/ts + run: bun install --frozen-lockfile + - name: Run TS snippet tests + working-directory: tests/ts + run: bun test diff --git a/.gitignore b/.gitignore index b968a29..71010bb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ __pycache__/ # Generated test modules tests/generated/ +tests/generated_ts/ +tests/ts/node_modules/ + # Coverage reports htmlcov/ diff --git a/index.mdx b/index.mdx index d0a8667..8fd061c 100644 --- a/index.mdx +++ b/index.mdx @@ -43,4 +43,4 @@ import { SiteSchema } from "/snippets/SiteSchema.mdx";



-If you were a user before 2026/01/05, you still have access to the previous version: [https://old-app.edenai.run/](https://old-app.edenai.run/). We'll continue supporting the old version until the end of 2026. If you're looking for the documentation, you can find it [here](https://old-docs.edenai.co) +If you were a user before 2026/01/05, you still have access to the previous version: [https://old-app.edenai.run/](https://old-app.edenai.run/). We'll continue supporting the old version until the end of 2026. If you're looking for the documentation, you can find it [here](https://www.edenai.co/docs/v2) diff --git a/tests/README.md b/tests/README.md index 13b8816..5102516 100644 --- a/tests/README.md +++ b/tests/README.md @@ -112,15 +112,54 @@ This also works with `` blocks — place the comment before the `` filename that `bun:test` routes to `test.skip`. `tests/generated_ts/fixtures/` is populated via the shared `populate_fixtures_dir()` helper so `image.jpg`, `document.pdf`, etc. are present. + +## Validators + +Beyond snippet execution, four validators enforce doc/API consistency. All run under the same `pytest -n auto`. + +| File | Checks | +|------|--------| +| `tests/config_validator.py` | Parses fenced JSON/YAML/TOML config blocks in tool-integration guides; validates Eden AI URL hosts + endpoint prefixes; cross-checks every `` `provider/model` `` string against the live inventory | +| `tests/api_reference_validator.py` | Fetches the 3 remote OpenAPI specs referenced in `docs.json`, asserts they're reachable and valid, cross-checks every `https://api.edenai.run/v[23]/…` URL in prose against the specs | +| `tests/model_provider_validator.py` | Scans every `.mdx` for backticked `provider/model` references; cross-checks against `/v3/models` + `/v3/info` + probed embeddings inventory | +| `tests/link_checker.py` | Extracts markdown links, JSX `href="…"`, ``, and bare URLs; verifies internal targets exist and external URLs return 2xx-3xx (or a non-404/410 4xx). Also checks every `docs.json` nav path resolves to an `.mdx` file | + +Model/provider lookup is powered by `tests/helpers/edenai_inventory.py`, a session-cached inventory of LLM models (`/v3/models`), expert models (`/v3/info`), and verified embeddings. + +Unknown `provider/model` references fail the test unless they match `DOCUMENTATION_PLACEHOLDERS` (e.g. `provider/model` used as a format placeholder), match an `UNAMBIGUOUS_MIME_PREFIXES` prefix (e.g. `application/json`), or resolve after `strip_tool_alias` removes a leading `edenai/` tool alias. There is no per-file allowlist. + ## CI (GitHub Actions) -The workflow at `.github/workflows/test-snippets.yml` runs on PRs that touch `v3/**/*.mdx` or `tests/**`: +The workflow at `.github/workflows/test-snippets.yml` runs on: +- PRs that touch `v3/**/*.mdx`, root `*.mdx`, `docs.json`, or `tests/**` +- Weekly cron (`0 6 * * 1`) +- Manual dispatch + +Two jobs, both with `cancel-in-progress: false` (session cleanup runs at pytest_sessionfinish; cancelling a started run orphans account resources): + +1. **Python Tests**: snippet execution + all four validators. +2. **TypeScript Tests**: installs bun + runs `bun test` in `tests/ts/`. + +Both consume `EDEN_AI_SANDBOX_TOKEN` and (Python job only) `EDEN_AI_PRODUCTION_TOKEN` from repository secrets. `EDEN_AI_BASE_URL` is set from the `EDEN_AI_BASE_URL` repository variable if defined (defaults to staging). -1. **Execution job**: runs execution tests with `EDEN_AI_SANDBOX_TOKEN` and `EDEN_AI_PRODUCTION_TOKEN` secrets +Python deps install from `requirements-lock.txt` for reproducibility. TS deps install from `tests/ts/bun.lock`. -Installs from `requirements-lock.txt` for reproducible builds. +## Disabling the doc-tests workflow -To set up: add `EDEN_AI_SANDBOX_TOKEN` and `EDEN_AI_PRODUCTION_TOKEN` as repository secrets in GitHub. +If the docs need to ship despite a failing test run (broken external link, upstream API drift, etc.), disable via GitHub UI: **Actions → Test Documentation Snippets → ⋯ → Disable workflow**. Mintlify's own build pipeline is independent, so the site continues to publish. ## Common Failure Patterns diff --git a/tests/api_reference_validator.py b/tests/api_reference_validator.py new file mode 100644 index 0000000..13c9ac8 --- /dev/null +++ b/tests/api_reference_validator.py @@ -0,0 +1,100 @@ +import re +from pathlib import Path +from urllib.parse import urlparse + +import pytest +import requests + +DOCS_ROOT = Path(__file__).resolve().parent.parent + +OPENAPI_SPEC_URLS = [ + "https://api.edenai.run/v3/docs/openapi.json", + "https://api.edenai.run/v2/info/splitted-schema/cost_management/openapi.json", + "https://api.edenai.run/v2/info/splitted-schema/user/openapi.json", +] + +ENDPOINT_URL_RE = re.compile( + r"https://api\.edenai\.run(?P/v[23][^\s,'\"`)>]*)" +) + +_NON_ENDPOINT_SUFFIXES = (".json", ".yaml", ".yml", ".txt", ".md") + + +@pytest.fixture(scope="session") +def openapi_specs() -> dict[str, dict]: + specs: dict[str, dict] = {} + for url in OPENAPI_SPEC_URLS: + resp = requests.get(url, timeout=30) + resp.raise_for_status() + specs[url] = resp.json() + return specs + + +@pytest.fixture(scope="session") +def openapi_paths(openapi_specs: dict[str, dict]) -> set[str]: + all_paths: set[str] = set() + for spec in openapi_specs.values(): + servers = spec.get("servers") or [{"url": ""}] + server_prefix = urlparse(servers[0].get("url", "")).path.rstrip("/") + for spec_path in spec.get("paths", {}).keys(): + all_paths.add((server_prefix + spec_path).rstrip("/")) + return all_paths + + +@pytest.mark.parametrize("spec_url", OPENAPI_SPEC_URLS) +def test_openapi_spec_reachable_and_valid(spec_url: str) -> None: + resp = requests.get(spec_url, timeout=30) + assert resp.status_code == 200, f"{spec_url} returned {resp.status_code}" + spec = resp.json() + assert isinstance(spec, dict), f"{spec_url} did not return a JSON object" + assert "openapi" in spec or "swagger" in spec, ( + f"{spec_url} is not an OpenAPI/Swagger document" + ) + paths = spec.get("paths") + assert isinstance(paths, dict) and paths, f"{spec_url} has no paths" + + +def _prose_pages() -> list[str]: + return sorted( + str(p.relative_to(DOCS_ROOT)) + for p in list(DOCS_ROOT.glob("v3/**/*.mdx")) + list(DOCS_ROOT.glob("*.mdx")) + if not str(p.relative_to(DOCS_ROOT)).startswith("api-reference/") + ) + + +def _is_endpoint_path(path: str) -> bool: + normalized = path.rstrip("/") + if normalized in ("/v2", "/v3"): + return False + if "..." in normalized: + return False + return not normalized.endswith(_NON_ENDPOINT_SUFFIXES) + + +def _matches_openapi_path(path: str, openapi_paths: set[str]) -> bool: + normalized = path.split("?")[0].split("#")[0].rstrip("/") + if normalized in openapi_paths: + return True + for spec_path in openapi_paths: + if "{" not in spec_path: + continue + pattern = re.sub(r"\{[^/]+\}", r"[^/]+", spec_path.rstrip("/")) + if re.fullmatch(pattern, normalized): + return True + return False + + +@pytest.mark.parametrize("page", _prose_pages()) +def test_prose_endpoint_urls_match_openapi(page: str, openapi_paths: set[str]) -> None: + content = (DOCS_ROOT / page).read_text(encoding="utf-8") + unknown: list[str] = [] + for match in ENDPOINT_URL_RE.finditer(content): + raw_path = match.group("path").split("?")[0].split("#")[0] + if not _is_endpoint_path(raw_path): + continue + if _matches_openapi_path(raw_path, openapi_paths): + continue + line = content[: match.start()].count("\n") + 1 + unknown.append(f"line {line}: {match.group(0)}") + if unknown: + pytest.fail(f"{page}:\n " + "\n ".join(unknown)) diff --git a/tests/config_validator.py b/tests/config_validator.py new file mode 100644 index 0000000..3407f6c --- /dev/null +++ b/tests/config_validator.py @@ -0,0 +1,143 @@ +import json +import os +import re +import tomllib +from pathlib import Path + +import pytest +import yaml + +from tests.helpers.edenai_inventory import get_model_inventory +from tests.helpers.model_names import ( + BACKTICKED_MODEL_RE, + DOCUMENTATION_PLACEHOLDERS, + strip_tool_alias, +) +from tests.snippet_extractor import SKIP_COMMENT_RE + +CONFIG_GUIDES = [ + "v3/integrations/bifrost.mdx", + "v3/integrations/claude-code.mdx", + "v3/integrations/cline.mdx", + "v3/integrations/codex-cli.mdx", + "v3/integrations/continue-dev.mdx", + "v3/integrations/hermes.mdx", + "v3/integrations/librechat.mdx", + "v3/integrations/n8n.mdx", + "v3/integrations/open-code-review.mdx", + "v3/integrations/open-webui.mdx", + "v3/integrations/openclaw.mdx", + "v3/integrations/opencode.mdx", + "v3/integrations/pi.mdx", +] + +DOCS_ROOT = Path(__file__).resolve().parent.parent + +ALLOWED_EDEN_HOSTS = { + "api.edenai.run", + "app.edenai.run", + "docs.edenai.co", + "app-edenai.instatus.com", +} + +KNOWN_API_ENDPOINT_PREFIXES = ("/v2", "/v3") + +FENCED_BLOCK_RE = re.compile( + r"^```(?P[a-zA-Z]+)(?:[ \t]+[^\n]*)?[ \t]*\n(?P.*?)^\s*```", + re.MULTILINE | re.DOTALL, +) + +EDEN_URL_RE = re.compile( + r"https://(?P[a-z0-9][a-z0-9\-]*(?:\.[a-z0-9\-]+)+)(?P/[^\s'\"`)>]*)?" +) + +CONFIG_PARSERS = { + "json": json.loads, + "yaml": yaml.safe_load, + "yml": yaml.safe_load, + "toml": tomllib.loads, +} + + +def parse_fenced_config_blocks(content: str) -> list[dict]: + blocks = [] + for m in FENCED_BLOCK_RE.finditer(content): + lang = m.group("lang").lower() + if lang not in CONFIG_PARSERS: + continue + preceding = content[: m.start()] + recent_lines = preceding.rsplit("\n", 3)[-3:] + if any(SKIP_COMMENT_RE.search(line) for line in recent_lines): + continue + blocks.append( + { + "lang": lang, + "body": m.group("body"), + "line": preceding.count("\n") + 1, + } + ) + return blocks + + +def find_eden_urls(content: str) -> list[dict]: + urls = [] + for m in EDEN_URL_RE.finditer(content): + urls.append( + { + "url": m.group(0), + "host": m.group("host"), + "path": (m.group("path") or "").split("?")[0].split("#")[0].rstrip("/"), + "line": content[: m.start()].count("\n") + 1, + } + ) + return urls + + +def find_provider_model_strings(content: str) -> list[str]: + return [ + f"{m.group('provider')}/{m.group('rest')}" + for m in BACKTICKED_MODEL_RE.finditer(content) + ] + + +@pytest.mark.parametrize("guide", CONFIG_GUIDES, ids=lambda p: Path(p).stem) +def test_config_guide(guide: str) -> None: + path = DOCS_ROOT / guide + assert path.exists(), f"Guide not found: {path}" + content = path.read_text(encoding="utf-8") + errors: list[str] = [] + + for block in parse_fenced_config_blocks(content): + parser = CONFIG_PARSERS[block["lang"]] + try: + parser(block["body"]) + except Exception as exc: + errors.append( + f"line {block['line']}: {block['lang']} config block failed to parse: {exc}" + ) + + for u in find_eden_urls(content): + if "edenai" not in u["host"] and "instatus" not in u["host"]: + continue + if u["host"] not in ALLOWED_EDEN_HOSTS: + errors.append( + f"line {u['line']}: unexpected Eden AI host `{u['host']}` in {u['url']}" + ) + continue + if u["host"] == "api.edenai.run" and u["path"]: + if not any(u["path"].startswith(prefix) for prefix in KNOWN_API_ENDPOINT_PREFIXES): + errors.append( + f"line {u['line']}: unknown API endpoint path `{u['path']}` in {u['url']}" + ) + + if os.environ.get("EDEN_AI_SANDBOX_API_TOKEN"): + inventory = get_model_inventory() + for pm in find_provider_model_strings(content): + if pm in DOCUMENTATION_PLACEHOLDERS: + continue + if pm in inventory or strip_tool_alias(pm) in inventory: + continue + errors.append(f"unknown model `{pm}` (not in live inventory)") + + if errors: + pytest.fail(f"{guide}:\n " + "\n ".join(errors)) diff --git a/tests/conftest.py b/tests/conftest.py index 6d9514f..c337e61 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,11 +21,8 @@ upload_test_file, ) from tests.helpers.file_generators import ( - large_jpeg, - minimal_jpeg, minimal_pdf, - minimal_png, - multipage_pdf, + populate_fixtures_dir, ) load_dotenv(Path(__file__).parent / ".env") @@ -64,28 +61,41 @@ def pytest_sessionstart(session: pytest.Session) -> None: state["test_file_id"] = test_file_id if os.environ.get("EDEN_AI_PRODUCTION_API_TOKEN"): - state["pre_existing_tokens"] = sorted(list_custom_token_names()) - - expire = (datetime.now() + timedelta(days=30)).isoformat() - for token_spec in [ - { - "name": "my-api-token", - "balance": "100.00", - "active_balance": True, - "expire_time": expire, - }, - {"name": "old-token"}, - ]: - try: - create_custom_token(**token_spec) - except requests.HTTPError as exc: - # Token may already exist — another run on the same account can - # create it first. Test `is None` explicitly: Response.__bool__ - # returns self.ok, so a 400 response is falsy and `not - # exc.response` was short-circuiting to True, re-raising the one - # case this handler exists to swallow. - if exc.response is None or exc.response.status_code != 400: - raise + try: + state["pre_existing_tokens"] = sorted(list_custom_token_names()) + + expire = (datetime.now() + timedelta(days=30)).isoformat() + for token_spec in [ + { + "name": "my-api-token", + "balance": "100.00", + "active_balance": True, + "expire_time": expire, + }, + {"name": "old-token"}, + ]: + try: + create_custom_token(**token_spec) + except requests.HTTPError as exc: + # Token may already exist — another run on the same account can + # create it first. Test `is None` explicitly: Response.__bool__ + # returns self.ok, so a 400 response is falsy and `not + # exc.response` was short-circuiting to True, re-raising the one + # case this handler exists to swallow. + if exc.response is None or exc.response.status_code != 400: + raise + except requests.HTTPError as exc: + # The /v2/user/custom_token/ admin endpoint requires a JWT session + # token, not an API key. When EDEN_AI_PRODUCTION_API_TOKEN holds an + # API key (the common case), skip custom-token setup instead of + # crashing pytest_sessionstart. Prod-token-only doc tests will then + # skip themselves via _PRODUCTION_TOKEN_FILES logic. + if exc.response is None or exc.response.status_code != 401: + raise + state.pop("pre_existing_tokens", None) + state["production_token_rejected"] = True + os.environ.pop("EDEN_AI_PRODUCTION_API_TOKEN", None) + print("\n[conftest] Skipping custom-token setup — production token rejected (401)") path = _shared_state_path(session.config) path.parent.mkdir(parents=True, exist_ok=True) @@ -136,6 +146,10 @@ def _load_shared_state(request): state = json.loads(path.read_text()) if "test_file_id" in state: os.environ.setdefault("_EDEN_TEST_FILE_ID", state["test_file_id"]) + # xdist workers don't see the controller's os.environ mutations, so + # propagate the rejected-token signal through the state file too. + if state.get("production_token_rejected"): + os.environ.pop("EDEN_AI_PRODUCTION_API_TOKEN", None) @pytest.fixture(scope="session") @@ -146,61 +160,11 @@ def fixtures_dir(request): with FileLock(str(d) + ".lock"): if not d.exists(): d.mkdir() - _populate_fixtures_dir(d) + populate_fixtures_dir(d) return d -def _populate_fixtures_dir(d: Path) -> None: - pdf_data = minimal_pdf() - for name in [ - "document.pdf", - "invoice.pdf", - "report.pdf", - "contract.pdf", - "quarterly-report.pdf", - "policy-document.pdf", - "research-paper.pdf", - "doc1.pdf", - "doc2.pdf", - "doc3.pdf", - "invoice1.pdf", - "invoice2.pdf", - "invoice3.pdf", - "doc.pdf", - ]: - (d / name).write_bytes(pdf_data) - - (d / "large-report.pdf").write_bytes(multipage_pdf(6)) - - jpeg_data = minimal_jpeg() - for name in [ - "image.jpg", - "photo.jpg", - "product.jpg", - "people.jpg", - "passport.jpg", - "receipt.jpg", - "user_upload.jpg", - "complex_document.jpg", - "user_photo.jpg", - ]: - (d / name).write_bytes(jpeg_data) - (d / "large-image.jpg").write_bytes(large_jpeg()) - - png_data = minimal_png() - for name in ["image.png", "screenshot.png"]: - (d / name).write_bytes(png_data) - - (d / "app.py").write_text("def main():\n print('hello')\n") - - (d / "document.txt").write_text( - "Eden AI is a platform that provides access to multiple AI providers " - "through a single API. It supports text analysis, image processing, " - "OCR, and many other AI features." - ) - - class HttpRecorder: """Stores the last HTTP request/response for debugging test failures.""" diff --git a/tests/helpers/edenai_inventory.py b/tests/helpers/edenai_inventory.py new file mode 100644 index 0000000..27f7e33 --- /dev/null +++ b/tests/helpers/edenai_inventory.py @@ -0,0 +1,81 @@ +import os +from functools import cache + +import requests + +_LLM_MODELS_ENDPOINT = "/v3/models" +_INFO_ENDPOINT = "/v3/info" +_EMBEDDINGS_ENDPOINT = "/v3/embeddings" + +_EMBEDDING_PROBE_CANDIDATES = frozenset( + { + "openai/text-embedding-3-small", + "openai/text-embedding-3-large", + "openai/text-embedding-ada-002", + "mistral/mistral-embed", + "cohere/embed-english-v3.0", + "cohere/embed-multilingual-v3.0", + } +) + + +def _base_url() -> str: + return os.environ.get("EDEN_AI_BASE_URL", "https://api.edenai.run") + + +def _auth_headers() -> dict[str, str]: + token = os.environ.get("EDEN_AI_SANDBOX_API_TOKEN") + if not token: + raise RuntimeError( + "EDEN_AI_SANDBOX_API_TOKEN is required to fetch Eden AI model inventory" + ) + return {"Authorization": f"Bearer {token}"} + + +@cache +def get_model_inventory() -> frozenset[str]: + base = _base_url() + headers = _auth_headers() + + llm_resp = requests.get(f"{base}{_LLM_MODELS_ENDPOINT}", headers=headers, timeout=30) + llm_resp.raise_for_status() + llm_entries = llm_resp.json().get("data") or [] + llm_models = { + entry["id"] for entry in llm_entries if entry.get("id") and "/" in entry["id"] + } + assert llm_models, f"{base}{_LLM_MODELS_ENDPOINT} returned no LLM models" + + info_resp = requests.get(f"{base}{_INFO_ENDPOINT}", headers=headers, timeout=30) + info_resp.raise_for_status() + info_data = info_resp.json() + + expert_ids: set[str] = set() + subfeature_paths: set[str] = set() + for feature in info_data.get("features") or []: + feature_name = feature.get("name") + if not feature_name: + continue + for subfeature in feature.get("subfeatures") or []: + subfeature_name = subfeature.get("name") + if not subfeature_name: + continue + subfeature_paths.add(f"{feature_name}/{subfeature_name}") + for model in subfeature.get("models") or []: + model_id = model.get("model") + if model_id: + expert_ids.add(model_id) + + assert expert_ids, f"{base}{_INFO_ENDPOINT} returned no expert-model IDs" + + verified_embeddings: set[str] = set() + for candidate in _EMBEDDING_PROBE_CANDIDATES: + probe = requests.post( + f"{base}{_EMBEDDINGS_ENDPOINT}", + headers=headers, + json={"model": candidate, "input": "x"}, + timeout=30, + ) + if probe.status_code == 200: + verified_embeddings.add(candidate) + + return frozenset(llm_models | expert_ids | subfeature_paths | verified_embeddings) diff --git a/tests/helpers/file_generators.py b/tests/helpers/file_generators.py index b40d0a5..865ad77 100644 --- a/tests/helpers/file_generators.py +++ b/tests/helpers/file_generators.py @@ -3,6 +3,7 @@ import io import struct import zlib +from pathlib import Path from PIL import Image from PyPDF2 import PdfReader, PdfWriter @@ -134,3 +135,60 @@ def _chunk(chunk_type: bytes, data: bytes) -> bytes: idat = _chunk(b"IDAT", zlib.compress(raw_data)) iend = _chunk(b"IEND", b"") return signature + ihdr + idat + iend + + +FIXTURE_PDFS = [ + "document.pdf", + "invoice.pdf", + "report.pdf", + "contract.pdf", + "quarterly-report.pdf", + "policy-document.pdf", + "research-paper.pdf", + "doc1.pdf", + "doc2.pdf", + "doc3.pdf", + "invoice1.pdf", + "invoice2.pdf", + "invoice3.pdf", + "doc.pdf", +] + +FIXTURE_JPEGS = [ + "image.jpg", + "photo.jpg", + "product.jpg", + "people.jpg", + "passport.jpg", + "receipt.jpg", + "user_upload.jpg", + "complex_document.jpg", + "user_photo.jpg", +] + +FIXTURE_PNGS = ["image.png", "screenshot.png"] + + +def populate_fixtures_dir(d: Path) -> None: + pdf_data = minimal_pdf() + for name in FIXTURE_PDFS: + (d / name).write_bytes(pdf_data) + + (d / "large-report.pdf").write_bytes(multipage_pdf(6)) + + jpeg_data = minimal_jpeg() + for name in FIXTURE_JPEGS: + (d / name).write_bytes(jpeg_data) + (d / "large-image.jpg").write_bytes(large_jpeg()) + + png_data = minimal_png() + for name in FIXTURE_PNGS: + (d / name).write_bytes(png_data) + + (d / "app.py").write_text("def main():\n print('hello')\n") + + (d / "document.txt").write_text( + "Eden AI is a platform that provides access to multiple AI providers " + "through a single API. It supports text analysis, image processing, " + "OCR, and many other AI features." + ) diff --git a/tests/helpers/model_names.py b/tests/helpers/model_names.py new file mode 100644 index 0000000..95677e1 --- /dev/null +++ b/tests/helpers/model_names.py @@ -0,0 +1,29 @@ +import re + +BACKTICKED_MODEL_RE = re.compile( + r"`(?P[a-z][a-z0-9\-]{1,30})/(?P[A-Za-z0-9][A-Za-z0-9\-_.:/]{0,120}?)`" +) + +DOCUMENTATION_PLACEHOLDERS = frozenset( + { + "provider/model", + "feature/subfeature", + "feature/subfeature/provider", + "feature/subfeature/provider/model", + "your/model", + "your/provider", + } +) + +UNAMBIGUOUS_MIME_PREFIXES = frozenset( + {"application", "multipart", "font", "message", "example"} +) + +TOOL_ALIAS_PREFIXES = frozenset({"edenai"}) + + +def strip_tool_alias(candidate: str) -> str: + head, sep, rest = candidate.partition("/") + if sep and head in TOOL_ALIAS_PREFIXES: + return rest + return candidate diff --git a/tests/link_checker.py b/tests/link_checker.py new file mode 100644 index 0000000..5792774 --- /dev/null +++ b/tests/link_checker.py @@ -0,0 +1,247 @@ +import ipaddress +import json +import os +import re +import socket +import time +from functools import cache +from pathlib import Path +from urllib.parse import urldefrag, urlparse + +import pytest +import requests + +DOCS_ROOT = Path(__file__).resolve().parent.parent + +FENCED_BLOCK_RE = re.compile(r"^```.*?^```", re.MULTILINE | re.DOTALL) +MARKDOWN_LINK_RE = re.compile(r"\[[^\]]*\]\((?P[^)\s]+)(?:\s+\"[^\"]*\")?\)") +JSX_HREF_RE = re.compile(r"\bhref\s*=\s*[\"'](?P[^\"'#][^\"']*)[\"']") +SCHEMA_PATH_RE = re.compile(r"\bpath\s*=\s*[\"'](?P[^\"']+)[\"']") +BARE_URL_RE = re.compile(r"(?https?://[^\s'\"`)>\]]+)") + +REQUEST_TIMEOUT_SECONDS = 10 +MAX_RETRIES = 3 +USER_AGENT = ( + "Mozilla/5.0 (compatible; edenai-docs-linkcheck/1.0; +https://docs.edenai.co)" +) + +SKIP_EXTERNAL_HOSTS = frozenset({"example.com"}) +MAX_REDIRECTS = 5 + +_classify_cache: dict[str, tuple[bool, str]] = {} + + +class _NonPublicDestination(Exception): + pass + + +def _classify_host(hostname: str) -> tuple[bool, str]: + cached = _classify_cache.get(hostname) + if cached is not None: + return cached + try: + infos = socket.getaddrinfo(hostname, None) + except socket.gaierror as exc: + # Not cached: DNS failures can be transient and must retry on next call. + return False, f"DNS lookup failed: {exc.strerror or exc}" + for info in infos: + try: + ip = ipaddress.ip_address(info[4][0]) + except ValueError: + continue + if not ip.is_global: + result = (False, f"resolves to non-global address {ip}") + _classify_cache[hostname] = result + return result + result = (True, "") + _classify_cache[hostname] = result + return result + + +def _request_following_validated_redirects( + method: str, url: str, headers: dict +) -> requests.Response: + current = url + for _ in range(MAX_REDIRECTS + 1): + parsed = urlparse(current) + if not parsed.hostname: + raise _NonPublicDestination(f"no hostname in {current}") + public, reason = _classify_host(parsed.hostname) + if not public: + raise _NonPublicDestination(reason) + resp = requests.request( + method, + current, + headers=headers, + allow_redirects=False, + timeout=REQUEST_TIMEOUT_SECONDS, + stream=(method == "GET"), + ) + if method == "GET": + resp.close() + if not (300 <= resp.status_code < 400): + return resp + location = resp.headers.get("Location") + if not location: + return resp + current = requests.compat.urljoin(current, location) + raise _NonPublicDestination(f"too many redirects (>{MAX_REDIRECTS})") + + +def _strip_fenced_blocks(content: str) -> str: + return FENCED_BLOCK_RE.sub("", content) + + +def _pages() -> list[str]: + return sorted( + str(p.relative_to(DOCS_ROOT)) + for p in list(DOCS_ROOT.glob("v3/**/*.mdx")) + list(DOCS_ROOT.glob("*.mdx")) + if not str(p.relative_to(DOCS_ROOT)).startswith("api-reference/") + ) + + +def _extract_links(page: str) -> list[dict]: + raw = (DOCS_ROOT / page).read_text(encoding="utf-8") + body = _strip_fenced_blocks(raw) + links: list[dict] = [] + seen: set[str] = set() + + def add(url: str, offset: int) -> None: + if url in seen: + return + seen.add(url) + links.append({"url": url, "line": raw[:offset].count("\n") + 1}) + + for match in MARKDOWN_LINK_RE.finditer(body): + add(match.group("url"), match.start()) + for match in JSX_HREF_RE.finditer(body): + add(match.group("url"), match.start()) + for match in SCHEMA_PATH_RE.finditer(body): + add(match.group("url"), match.start()) + for match in BARE_URL_RE.finditer(body): + add(match.group("url"), match.start()) + return links + + +def _resolve_internal(source_page: str, link: str) -> Path: + target, _ = urldefrag(link.split("?", 1)[0]) + if not target: + return DOCS_ROOT / source_page + + if target.startswith("/"): + candidates = [DOCS_ROOT / target.lstrip("/")] + elif target.startswith(("./", "../")): + candidates = [(DOCS_ROOT / source_page).parent / target] + else: + candidates = [ + DOCS_ROOT / target, + (DOCS_ROOT / source_page).parent / target, + ] + + for base in candidates: + if base.suffix in {".mdx", ".md"} and base.exists(): + return base + for suffix in (".mdx", ".md"): + with_ext = base.with_suffix(suffix) + if with_ext.exists(): + return with_ext + return candidates[0] + + +def _is_external(link: str) -> bool: + parsed = urlparse(link) + return parsed.scheme in {"http", "https"} + + +def _is_checkable(link: str) -> bool: + parsed = urlparse(link) + if parsed.scheme in {"mailto", "tel", "javascript"}: + return False + if not parsed.scheme and not parsed.netloc and not parsed.path: + return False + if parsed.scheme in {"http", "https"} and parsed.hostname in SKIP_EXTERNAL_HOSTS: + return False + return True + + +DEFINITELY_BROKEN_STATUSES = frozenset({404, 410}) + + +@cache +def _check_external(url: str) -> tuple[bool, str]: + headers = {"User-Agent": USER_AGENT} + last_status = "no attempt" + for attempt in range(MAX_RETRIES): + try: + resp = _request_following_validated_redirects("HEAD", url, headers) + if resp.status_code in {400, 403, 405, 501}: + resp = _request_following_validated_redirects("GET", url, headers) + except _NonPublicDestination as exc: + return False, str(exc) + except requests.RequestException as exc: + last_status = f"network error: {exc.__class__.__name__}" + time.sleep(2**attempt) + continue + if resp.status_code == 429: + wait = int(resp.headers.get("Retry-After", "1")) + time.sleep(min(wait, 5)) + continue + if resp.status_code in DEFINITELY_BROKEN_STATUSES: + return False, f"HTTP {resp.status_code}" + if 500 <= resp.status_code < 600: + last_status = f"HTTP {resp.status_code}" + time.sleep(2**attempt) + continue + return True, str(resp.status_code) + return False, last_status + + +@pytest.mark.parametrize("page", _pages()) +def test_page_links(page: str) -> None: + broken: list[str] = [] + for link in _extract_links(page): + url = link["url"] + if not _is_checkable(url): + continue + if _is_external(url): + ok, detail = _check_external(url) + if not ok: + broken.append(f"line {link['line']}: external {url} ({detail})") + else: + target = _resolve_internal(page, url) + if not target.exists(): + broken.append(f"line {link['line']}: internal {url} -> {target.relative_to(DOCS_ROOT)} (missing)") + if broken: + pytest.fail(f"{page}:\n " + "\n ".join(broken)) + + +def _collect_docs_json_pages(node: object) -> list[str]: + found: list[str] = [] + if isinstance(node, dict): + for key, value in node.items(): + if key == "pages" and isinstance(value, list): + for item in value: + if isinstance(item, str): + found.append(item) + else: + found.extend(_collect_docs_json_pages(item)) + elif key != "openapi": + found.extend(_collect_docs_json_pages(value)) + elif isinstance(node, list): + for item in node: + found.extend(_collect_docs_json_pages(item)) + return found + + +def test_docs_json_nav_paths() -> None: + docs_json = json.loads((DOCS_ROOT / "docs.json").read_text(encoding="utf-8")) + broken: list[str] = [] + for path in _collect_docs_json_pages(docs_json): + if path.startswith(("http://", "https://", "#")): + continue + candidate_mdx = DOCS_ROOT / f"{path.lstrip('/')}.mdx" + candidate_md = DOCS_ROOT / f"{path.lstrip('/')}.md" + if not candidate_mdx.exists() and not candidate_md.exists(): + broken.append(f"docs.json nav path `{path}` -> file missing") + if broken: + pytest.fail("docs.json:\n " + "\n ".join(broken)) diff --git a/tests/model_provider_validator.py b/tests/model_provider_validator.py new file mode 100644 index 0000000..d5c9c1c --- /dev/null +++ b/tests/model_provider_validator.py @@ -0,0 +1,63 @@ +import os +from pathlib import Path + +import pytest + +from tests.helpers.edenai_inventory import get_model_inventory +from tests.helpers.model_names import ( + BACKTICKED_MODEL_RE, + DOCUMENTATION_PLACEHOLDERS, + UNAMBIGUOUS_MIME_PREFIXES, + strip_tool_alias, +) + +DOCS_ROOT = Path(__file__).resolve().parent.parent + + +def _pages() -> list[str]: + return sorted( + str(p.relative_to(DOCS_ROOT)) + for p in list(DOCS_ROOT.glob("v3/**/*.mdx")) + list(DOCS_ROOT.glob("*.mdx")) + if not str(p.relative_to(DOCS_ROOT)).startswith("api-reference/") + ) + + +@pytest.mark.parametrize("page", _pages()) +def test_model_and_provider_names(page: str) -> None: + if not os.environ.get("EDEN_AI_SANDBOX_API_TOKEN"): + pytest.skip("EDEN_AI_SANDBOX_API_TOKEN not set") + + inventory = get_model_inventory() + known_prefixes = {entry.split("/", 1)[0] for entry in inventory} + content = (DOCS_ROOT / page).read_text(encoding="utf-8") + + unknown: list[str] = [] + seen: set[str] = set() + + for match in BACKTICKED_MODEL_RE.finditer(content): + provider_or_feature = match.group("provider") + candidate = f"{provider_or_feature}/{match.group('rest')}" + if candidate in seen: + continue + seen.add(candidate) + + if candidate in inventory or strip_tool_alias(candidate) in inventory: + continue + if candidate in DOCUMENTATION_PLACEHOLDERS: + continue + if provider_or_feature in UNAMBIGUOUS_MIME_PREFIXES: + continue + + line = content[: match.start()].count("\n") + 1 + if provider_or_feature in known_prefixes: + unknown.append( + f"line {line}: unknown model `{candidate}` " + f"(prefix `{provider_or_feature}` is known)" + ) + else: + unknown.append( + f"line {line}: unknown model or provider `{candidate}`" + ) + + if unknown: + pytest.fail(f"{page}:\n " + "\n ".join(unknown)) diff --git a/tests/snippet_extractor.py b/tests/snippet_extractor.py index ca32961..1994cb2 100644 --- a/tests/snippet_extractor.py +++ b/tests/snippet_extractor.py @@ -5,12 +5,14 @@ from filelock import FileLock +from tests.helpers.file_generators import populate_fixtures_dir + CODE_BLOCK_RE = re.compile( r"^```python(?:[ \t]+\S+)?[ \t]*\n(.*?)^\s*```", re.MULTILINE | re.DOTALL, ) -_SKIP_COMMENT_RE = re.compile(r"\{/\*\s*skip-test\s*\*/\}") +SKIP_COMMENT_RE = re.compile(r"\{/\*\s*skip-test\s*\*/\}") _SANDBOX_TOKEN_VAR = "EDEN_AI_SANDBOX_API_TOKEN" _PRODUCTION_TOKEN_VAR = "EDEN_AI_PRODUCTION_API_TOKEN" @@ -72,7 +74,7 @@ def extract_python_blocks(mdx_path: Path) -> list[dict]: for match in CODE_BLOCK_RE.finditer(content): preceding = content[: match.start()] recent_lines = preceding.rsplit("\n", 3)[-3:] - skip = any(_SKIP_COMMENT_RE.search(line) for line in recent_lines) + skip = any(SKIP_COMMENT_RE.search(line) for line in recent_lines) code = match.group(1) line = preceding.count("\n") + 2 blocks.append({"code": code, "line": line, "skip": skip}) @@ -227,6 +229,146 @@ def extract_all() -> list[dict]: return results +TS_FENCE_RE = re.compile( + r"^```(?Ptypescript|ts|tsx|javascript|js|jsx)" + r"(?:[ \t]+[^\n]*)?[ \t]*\n(?P.*?)^\s*```", + re.MULTILINE | re.DOTALL, +) + +TS_GUIDES: list[str] = [ + "v3/integrations/openai-sdk-typescript.mdx", + "v3/integrations/langchain.mdx", + "v3/integrations/pi.mdx", +] + +GENERATED_TS_DIR = Path(__file__).resolve().parent / "generated_ts" + +_TS_TOKEN_EXPR = f"process.env.{_SANDBOX_TOKEN_VAR}" +_TS_BASE_URL_EXPR = ( + f'process.env.EDEN_AI_BASE_URL ?? "{_DEFAULT_BASE_URL}"' +) + +_TS_REWRITES: list[tuple[re.Pattern[str], str]] = [ + (re.compile(r"""(['"`])YOUR_(?:EDEN_AI_)?API_KEY\1"""), _TS_TOKEN_EXPR), + ( + re.compile(r'"Bearer\s+YOUR_(?:EDEN_AI_)?API_KEY"'), + f"`Bearer ${{{_TS_TOKEN_EXPR}}}`", + ), + (re.compile(r"process\.env\.EDEN_AI_API_KEY"), _TS_TOKEN_EXPR), + ( + re.compile(r"""(['"])https://api\.edenai\.run(?P[^'"]*)\1"""), + f"`${{{_TS_BASE_URL_EXPR}}}\\g`", + ), + ( + re.compile(r"https://api\.edenai\.run"), + f"${{{_TS_BASE_URL_EXPR}}}", + ), + ( + re.compile(rf'"{_PLACEHOLDER_FILE_ID}"'), + f'(process.env._EDEN_TEST_FILE_ID ?? "{_PLACEHOLDER_FILE_ID}")', + ), +] + +_STRIP_TS_NON_NULL_RE = re.compile(rf"{re.escape(_TS_TOKEN_EXPR)}!") + +_CJS_RE = re.compile( + r"^\s*(?:const|let|var)\s+\w+\s*=\s*require\s*\(|^\s*module\.exports\b", + re.MULTILINE, +) +_JSX_RETURN_RE = re.compile(r"\breturn\s*\(?\s*<[A-Za-z]") + + +def _ts_extension(lang: str, body: str) -> str: + if _CJS_RE.search(body): + return "cjs" + if _JSX_RETURN_RE.search(body): + return "tsx" + if lang in {"tsx", "jsx"}: + return "tsx" + if lang in {"javascript", "js"}: + return "mjs" + return "ts" + + +def rewrite_ts_snippet(code: str, ext: str) -> str: + for pattern, replacement in _TS_REWRITES: + code = pattern.sub(replacement, code) + if ext in {"js", "jsx", "mjs", "cjs"}: + code = _STRIP_TS_NON_NULL_RE.sub(_TS_TOKEN_EXPR, code) + return code + + +def extract_ts_blocks(mdx_path: Path) -> list[dict]: + content = mdx_path.read_text(encoding="utf-8") + blocks: list[dict] = [] + for match in TS_FENCE_RE.finditer(content): + preceding = content[: match.start()] + recent_lines = preceding.rsplit("\n", 3)[-3:] + skip = any(SKIP_COMMENT_RE.search(line) for line in recent_lines) + lang = match.group("lang") + body = match.group("body") + blocks.append( + { + "code": body, + "line": preceding.count("\n") + 2, + "skip": skip, + "ext": _ts_extension(lang, body), + } + ) + return blocks + + +_TS_EXTRACT_LOCK = GENERATED_TS_DIR / ".extract.lock" + + +TS_FIXTURES_DIR = GENERATED_TS_DIR / "fixtures" + + +def extract_all_ts() -> list[dict]: + GENERATED_TS_DIR.mkdir(parents=True, exist_ok=True) + TS_FIXTURES_DIR.mkdir(parents=True, exist_ok=True) + results: list[dict] = [] + + with FileLock(str(_TS_EXTRACT_LOCK)): + (GENERATED_TS_DIR / "package.json").write_text('{"type":"module"}\n') + populate_fixtures_dir(TS_FIXTURES_DIR) + + # The test runner discovers files by globbing this directory, so + # snippets left over from a previous run would still execute after + # their source block is edited out of the docs. + for stale in GENERATED_TS_DIR.glob("*__block_*"): + stale.unlink() + + for rel in TS_GUIDES: + mdx_path = DOCS_ROOT / rel + if not mdx_path.exists(): + raise FileNotFoundError(f"Guide not found: {mdx_path}") + blocks = extract_ts_blocks(mdx_path) + if not blocks: + continue + + stem = sanitize_filename(mdx_path) + files: list[dict] = [] + for i, block in enumerate(blocks, start=1): + skip_marker = ".skip" if block["skip"] else "" + name = f"{stem}__block_{i}{skip_marker}.{block['ext']}" + path = GENERATED_TS_DIR / name + path.write_text(rewrite_ts_snippet(block["code"], block["ext"])) + files.append( + { + "index": i, + "line": block["line"], + "skip": block["skip"], + "ext": block["ext"], + "path": str(path), + } + ) + + results.append({"source_mdx": rel, "module_stem": stem, "files": files}) + + return results + + if __name__ == "__main__": results = extract_all() total_snippets = sum(r["snippet_count"] for r in results) @@ -236,3 +378,13 @@ def extract_all() -> list[dict]: for r in results: flag = " [has input()]" if r["has_input"] else "" print(f" {r['source_mdx']}: {r['snippet_count']} snippets{flag}") + + ts_results = extract_all_ts() + ts_total = sum(len(r["files"]) for r in ts_results) + print() + print(f"Extracted {ts_total} TS/JS snippets from {len(ts_results)} guide(s)") + for r in ts_results: + print(f" {r['source_mdx']}: {len(r['files'])} snippets") + for f in r["files"]: + mark = " [skip]" if f["skip"] else "" + print(f" block {f['index']:>2} ({f['ext']}, line {f['line']}){mark}") diff --git a/tests/ts/bun.lock b/tests/ts/bun.lock new file mode 100644 index 0000000..4ddd040 --- /dev/null +++ b/tests/ts/bun.lock @@ -0,0 +1,569 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "dependencies": { + "@earendil-works/pi-coding-agent": "^0.84.1", + "@langchain/core": "^0.3.30", + "@langchain/openai": "^0.3.16", + "@types/bun": "latest", + "@types/express": "^4.17.21", + "@types/react": "^18.3.11", + "dotenv": "^16.4.5", + "express": "^4.21.0", + "form-data": "^4.0.1", + "node-fetch": "^3.3.2", + "openai": "^4.68.0", + "react": "^18.3.1", + }, + }, + }, + "packages": { + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.91.1", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw=="], + + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + + "@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/eventstream-handler-node": "^3.972.16", "@aws-sdk/middleware-eventstream": "^3.972.12", "@aws-sdk/middleware-websocket": "^3.972.19", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.977.6", "", { "dependencies": { "@aws-sdk/types": "^3.974.2", "@aws-sdk/xml-builder": "^3.972.37", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.31.1", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.16.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-QiaJV4/zDrB4ZY2mfeSXSzSTc36W16sZXcGz+SPFk0CJ26gziO0cS+4LjJUMAbdeeBOvS0k0Aq1cZpfGdUXxSw=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.67", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-rcIpk5kxUqDaaNa6Xk23pQ6ViY7jlqzmfFWCahQcBT97ddXaXYYwzCen9Tz1Jvo6aJft6wDl5bN44/Jw5B4oLA=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.69", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/node-http-handler": "^4.9.13", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-nggwJtZ4eeNsUw5IeWBMXsi1ryct5idi0K+/SCRF3kybLubOMaNTb3XCihXpWMiVpyzyPeIrl0zTkzhBH9porA=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.12", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/credential-provider-env": "^3.972.67", "@aws-sdk/credential-provider-http": "^3.972.69", "@aws-sdk/credential-provider-login": "^3.972.74", "@aws-sdk/credential-provider-process": "^3.972.67", "@aws-sdk/credential-provider-sso": "^3.973.11", "@aws-sdk/credential-provider-web-identity": "^3.972.73", "@aws-sdk/nested-clients": "^3.997.41", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-pNEf/OeyN5X3VmLKlgSO6TqaWmW10CvI3TfwL1XhsuhYjSLT2VDaxFnCPHnOeQXSaFisMX4jNhpETriqN8DOmg=="], + + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.74", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/nested-clients": "^3.997.41", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-0AQfDcf99TNmqVKv0owHrw/TQs6i4ZE5t9qmz6NvO53bE/sA/tpXhXL9AAcEP1qHc6Zzjd1UMb69+/9zdhvY3g=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.78", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.67", "@aws-sdk/credential-provider-http": "^3.972.69", "@aws-sdk/credential-provider-ini": "^3.973.12", "@aws-sdk/credential-provider-process": "^3.972.67", "@aws-sdk/credential-provider-sso": "^3.973.11", "@aws-sdk/credential-provider-web-identity": "^3.972.73", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/credential-provider-imds": "^4.4.16", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-OgPAnfvbGAMWac6yvxJ1ihslrvDpPVwR68D2csospdNCCyPvHk9JLzYKwz48SNiS1T2znDwHauywRKRFfpyYng=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.67", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-IlUEejorGTWKb4/Dm7K5Yw4QxUmXLThLhrvBmzVBqZFTbW72cv9LTcITmo1dsnYriALE4h68mOq4LB99x6sQ7Q=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.11", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/nested-clients": "^3.997.41", "@aws-sdk/token-providers": "3.1103.0", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-gAQBkBZxUB84d71+pPcI9L+jh2ujhuAVxc/4FgGiWFDjkPBlMKxzd5XDtkSXTFX8Ro7ansnT88+XadasxMeCRw=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.73", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/nested-clients": "^3.997.41", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-SnlEmQa6SjOgs6iOPLUQl1Eyq4AKiAdPQlkOhFhqNfDtDCwibMGvL6QlkSmf3o6vAUSImzdPCxowT5dfQUZP1A=="], + + "@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.31", "", { "dependencies": { "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-/BRzvkp46mF6eXBL/l9WKPQQfifLlUPaWli6n9/T/WDLUg8he7TCyuNFnk6RvHP5j5W/kMj5Gxw7W778LJaXDA=="], + + "@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.972.26", "", { "dependencies": { "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-2eIvouTZoxPu5ClHY6ij13De1yhY8Rmllt0dlGeBNXX3wmR7fU1pvMCGb50fKm1GxcuntWK0t1T0cMjTyDoUQA=="], + + "@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.49", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-wGYJvu1/stdWuzGcNyh44u8aY7ArU8XFrMesBlzIYn70HWVCRBNEngQexDuPIMu0NEgsKGKmTc0uvnS/bF7KWw=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.41", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/signature-v4-multi-region": "^3.996.43", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/fetch-http-handler": "^5.6.13", "@smithy/node-http-handler": "^4.9.13", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-RDHqPGQWlF6tatA/Tp3rg6oIwtgN9IVderxE+9av2Y93Dfyu+mO1hZ5Bu2jpfZg2rwdNbsssnwM+sLafIczMlQ=="], + + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.43", "", { "dependencies": { "@aws-sdk/types": "^3.974.2", "@smithy/signature-v4": "^5.6.12", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-lKekx8bLBXSv4O+cslk9Zfnw2XKSkWBs3uWL5QGhH2ZAQfNS7FE0vcSSN2vD/AhxX54ZTywWxR4STThoeOXlBA=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.974.2", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.8", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.37", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-zKq4HQum8JwDyEuyfuI4bbiAcU0KxP6qy+9PR/IsR92IyE/DaBAikzAS50tjxip4bqIIANpCcG+Yyj6CVhXupg=="], + + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="], + + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + + "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="], + + "@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.84.1", "", { "dependencies": { "@earendil-works/pi-ai": "^0.84.1", "@earendil-works/pi-telemetry": "^0.84.1", "diff": "8.0.4", "ignore": "7.0.5", "typebox": "1.3.7", "yaml": "2.9.0" } }, "sha512-evyzXYWCLQGmcaBYHlmSku02r8qoN4SGI60GZABo6iV+H+nqX+P9ud8fEZ4GmRq9mUSREvvfX+w9dA9ThF9C6w=="], + + "@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.84.1", "", { "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@earendil-works/pi-telemetry": "^0.84.1", "@google/genai": "1.52.0", "@mistralai/mistralai": "2.2.6", "@opentelemetry/api": "1.9.0", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", "typebox": "1.3.7" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-wMsAdJMxuNri08vLqTyYVI201DQQezGhPSTkzYsHdw5dYX3rCNwEmSvpaAwhi7ELKI/2tE/CEgSWg/6iRxSgdQ=="], + + "@earendil-works/pi-client": ["@earendil-works/pi-client@0.84.1", "", { "dependencies": { "@earendil-works/pi-protocol": "^0.84.1" } }, "sha512-/V5hGHE4Zq+jG0GtwIB9PyBUOGd6gBLZ7lkQYFKchKnxYHeH3rmWC5xw4kpnZKKBuBuFTdLVbU9vEjlAGMMb2A=="], + + "@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.84.1", "", { "dependencies": { "@earendil-works/pi-agent-core": "^0.84.1", "@earendil-works/pi-ai": "^0.84.1", "@earendil-works/pi-client": "^0.84.1", "@earendil-works/pi-protocol": "^0.84.1", "@earendil-works/pi-tui": "^0.84.1", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", "diff": "8.0.4", "glob": "13.0.6", "grok-mermaid": "0.2.2", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.5", "jiti": "2.7.0", "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", "typebox": "1.3.7", "undici": "8.9.0", "yaml": "2.9.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "pi": "dist/cli.js" } }, "sha512-ncAqFrG+iybuPGOhMiZoEHkEzTpJgz3guYD32pD+M7ucc0WeHmauP6wa7qwP8V/KWvsZDVNa5XGsdZ7fkC7w7A=="], + + "@earendil-works/pi-protocol": ["@earendil-works/pi-protocol@0.84.1", "", { "dependencies": { "typebox": "1.3.7" } }, "sha512-Ox1pciyeSPGEEUcxvR0/dJcrY7C6hrEGA8y71rOsvSIUlXN1Cbp/be/eoL71OGDBk5O97TeQPfWN6Ju/2Ehjww=="], + + "@earendil-works/pi-telemetry": ["@earendil-works/pi-telemetry@0.84.1", "", {}, "sha512-180/xGJtsq7IoR3p9EKWjRd0e9M4DkxInhlo9xyD7prDC7Qrhqq+nhvwrW0lFjPfXcEI2FSHmGCSyvSJE9GsaQ=="], + + "@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.84.1", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.5" } }, "sha512-udeXFbgEhJ6JiB0uguwNVNkDy2FENfmtQwPcY+/iJ8GWeq18wkal1tKqa5YyeH0IqtX1vG0cGh8zfSYzyzVuLA=="], + + "@google/genai": ["@google/genai@1.52.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q=="], + + "@langchain/core": ["@langchain/core@0.3.80", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.3.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA=="], + + "@langchain/openai": ["@langchain/openai@0.3.17", "", { "dependencies": { "js-tiktoken": "^1.0.12", "openai": "^4.77.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" }, "peerDependencies": { "@langchain/core": ">=0.3.29 <0.4.0" } }, "sha512-uw4po32OKptVjq+CYHrumgbfh4NuD7LqyE+ZgqY9I/LrLc6bHLMc+sisHmI17vgek0K/yqtarI0alPJbzrwyag=="], + + "@mariozechner/clipboard": ["@mariozechner/clipboard@0.3.9", "", { "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.9", "@mariozechner/clipboard-darwin-universal": "0.3.9", "@mariozechner/clipboard-darwin-x64": "0.3.9", "@mariozechner/clipboard-linux-arm64-gnu": "0.3.9", "@mariozechner/clipboard-linux-arm64-musl": "0.3.9", "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-gnu": "0.3.9", "@mariozechner/clipboard-linux-x64-musl": "0.3.9", "@mariozechner/clipboard-win32-arm64-msvc": "0.3.9", "@mariozechner/clipboard-win32-x64-msvc": "0.3.9" } }, "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA=="], + + "@mariozechner/clipboard-darwin-arm64": ["@mariozechner/clipboard-darwin-arm64@0.3.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ=="], + + "@mariozechner/clipboard-darwin-universal": ["@mariozechner/clipboard-darwin-universal@0.3.9", "", { "os": "darwin" }, "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ=="], + + "@mariozechner/clipboard-darwin-x64": ["@mariozechner/clipboard-darwin-x64@0.3.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg=="], + + "@mariozechner/clipboard-linux-arm64-gnu": ["@mariozechner/clipboard-linux-arm64-gnu@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw=="], + + "@mariozechner/clipboard-linux-arm64-musl": ["@mariozechner/clipboard-linux-arm64-musl@0.3.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ=="], + + "@mariozechner/clipboard-linux-riscv64-gnu": ["@mariozechner/clipboard-linux-riscv64-gnu@0.3.9", "", { "os": "linux", "cpu": "none" }, "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw=="], + + "@mariozechner/clipboard-linux-x64-gnu": ["@mariozechner/clipboard-linux-x64-gnu@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw=="], + + "@mariozechner/clipboard-linux-x64-musl": ["@mariozechner/clipboard-linux-x64-musl@0.3.9", "", { "os": "linux", "cpu": "x64" }, "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ=="], + + "@mariozechner/clipboard-win32-arm64-msvc": ["@mariozechner/clipboard-win32-arm64-msvc@0.3.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ=="], + + "@mariozechner/clipboard-win32-x64-msvc": ["@mariozechner/clipboard-win32-x64-msvc@0.3.9", "", { "os": "win32", "cpu": "x64" }, "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA=="], + + "@mistralai/mistralai": ["@mistralai/mistralai@2.2.6", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.40.0", "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.1", "", {}, "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1" } }, "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.2", "", {}, "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug=="], + + "@silvia-odwyer/photon-node": ["@silvia-odwyer/photon-node@0.3.4", "", {}, "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA=="], + + "@smithy/core": ["@smithy/core@3.31.1", "", { "dependencies": { "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-CyogUINxvi7C7LDsh8Syo6hVJOT9ckz4rG8dRZfTJ8r91HkMY59PnNooaj7WcHyxEkxPfBAmbgztZU+xTo76lg=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.4.16", "", { "dependencies": { "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-QfuLWAkLzptffFW980AFeHZFdqds2B64rpEd3uJ6lgs3xVn9QegGMUgUcj+4d7dRrAsya3r58ZKpku97WcFb4w=="], + + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.6.13", "", { "dependencies": { "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-4fW86pEUOMbrD5nkbyl/tTvPHHWJFbuB2odl6ps9lWfHoXf9HWh3Q/Smh59qH1g7+c/BSZghX6bbUk4gsiMs8A=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.6.12", "", { "dependencies": { "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-I6KLtq3H0qqSuV9vLglfi8puHqzygzWHOnI4z/Rdoo+q50vvo18vBRdPAvvEtcaKROz7Zn6qnPa14kRfPH6PcQ=="], + + "@smithy/types": ["@smithy/types@4.16.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg=="], + + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], + + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/express": ["@types/express@4.17.25", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="], + + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.9", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg=="], + + "@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="], + + "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + + "@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], + + "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], + + "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], + + "@types/qs": ["@types/qs@6.15.1", "", {}, "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw=="], + + "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + + "@types/react": ["@types/react@18.3.31", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw=="], + + "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], + + "@types/send": ["@types/send@1.2.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ=="], + + "@types/serve-static": ["@types/serve-static@1.15.10", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw=="], + + "@types/uuid": ["@types/uuid@10.0.0", "", {}, "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ=="], + + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "body-parser": ["body-parser@1.20.6", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g=="], + + "bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="], + + "brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "console-table-printer": ["console-table-printer@2.16.1", "", { "dependencies": { "simple-wcswidth": "^1.1.2" } }, "sha512-Sc9FRJ4O9xKGNrvulNdPfK5SyBcZ6lcaRnDE4AQ/uw6IDtjHhsqyzzqcnMikjyGaiOOF2tNOKoBhbVjRvFy9Lw=="], + + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + + "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + + "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], + + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + + "express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + + "finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], + + "form-data": ["form-data@4.0.6", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="], + + "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="], + + "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="], + + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gaxios": ["gaxios@7.3.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-RB5vLV+vvQeoFPCX4QMK6/hjVkbIamPp1QSUD0CiZcnj12qbpiL+pLbYtgD+oZkWl0tl9z+o2Utp+MpM3QRhBA=="], + + "gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + + "google-auth-library": ["google-auth-library@10.9.1", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw=="], + + "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "grok-mermaid": ["grok-mermaid@0.2.2", "", {}, "sha512-XcJEP5dDC8liHBh52mlLjU18fNvu1ckFsu0QpIG3+APZ270fsj9wxpiA6cOURmbUEuoMVgjbC2+UYgTdCqqgzA=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], + + "hosted-git-info": ["hosted-git-info@9.0.3", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="], + + "json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="], + + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + + "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], + + "langsmith": ["langsmith@0.3.87", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], + + "marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], + + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "openai": ["openai@4.104.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="], + + "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="], + + "p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="], + + "p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="], + + "p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + + "path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="], + + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + + "protobufjs": ["protobufjs@7.6.5", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "qs": ["qs@6.15.3", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], + + "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], + + "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], + + "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "simple-wcswidth": ["simple-wcswidth@1.1.2", "", {}, "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + + "typebox": ["typebox@1.3.7", "", {}, "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg=="], + + "undici": ["undici@8.9.0", "", {}, "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA=="], + + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + + "uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], + + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + + "@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.9.13", "", { "dependencies": { "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-Nmd/Nl35zfYrd+a6OO2cDJb3GPh9bgTjIUhcM+JFfjpp8/osCgboDV5nCT1I01Pv6R13eSKDKLSoVa5ZB6Zsfw=="], + + "@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1103.0", "", { "dependencies": { "@aws-sdk/core": "^3.977.6", "@aws-sdk/nested-clients": "^3.997.41", "@aws-sdk/types": "^3.974.2", "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-N4wy26MNn31ItGVHYHPrEuCIFY4MBBjC+C5v1lJKqIUSA7OZBdhleCY53zCCrXn27hsk7YNOaTuhQu807S4AfQ=="], + + "@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.9.13", "", { "dependencies": { "@smithy/core": "^3.31.1", "@smithy/types": "^4.16.1", "tslib": "^2.6.2" } }, "sha512-Nmd/Nl35zfYrd+a6OO2cDJb3GPh9bgTjIUhcM+JFfjpp8/osCgboDV5nCT1I01Pv6R13eSKDKLSoVa5ZB6Zsfw=="], + + "@earendil-works/pi-ai/openai": ["openai@6.26.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA=="], + + "@types/serve-static/@types/send": ["@types/send@0.17.6", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og=="], + + "formdata-node/web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], + + "http-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "https-proxy-agent/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "humanize-ms/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "langsmith/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "langsmith/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "openai/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "p-retry/retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], + + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "http-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "langsmith/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + } +} diff --git a/tests/ts/bunfig.toml b/tests/ts/bunfig.toml new file mode 100644 index 0000000..63ff9cb --- /dev/null +++ b/tests/ts/bunfig.toml @@ -0,0 +1,2 @@ +[test] +preload = ["./globalSetup.ts"] diff --git a/tests/ts/globalSetup.ts b/tests/ts/globalSetup.ts new file mode 100644 index 0000000..dd2bb47 --- /dev/null +++ b/tests/ts/globalSetup.ts @@ -0,0 +1,30 @@ +const repoRoot = `${import.meta.dir}/../..`; +const envFile = Bun.file(`${repoRoot}/tests/.env`); + +if (await envFile.exists()) { + for (const line of (await envFile.text()).split('\n')) { + const eq = line.indexOf('='); + if (eq === -1) continue; + const key = line.slice(0, eq).trim(); + if (!key || key.startsWith('#')) continue; + const value = line.slice(eq + 1).trim().replace(/^['"]|['"]$/g, ''); + process.env[key] ??= value; + } +} + +const venvPython = `${repoRoot}/.venv/bin/python`; +const python = (await Bun.file(venvPython).exists()) + ? venvPython + : Bun.which('python3') ?? Bun.which('python'); + +if (!python) throw new Error('No Python interpreter found'); + +const result = Bun.spawnSync([python, '-m', 'tests.snippet_extractor'], { + cwd: repoRoot, + stdout: 'inherit', + stderr: 'inherit', +}); + +if (!result.success) { + throw new Error(`Snippet extraction failed (exit code ${result.exitCode})`); +} diff --git a/tests/ts/package.json b/tests/ts/package.json new file mode 100644 index 0000000..98f381e --- /dev/null +++ b/tests/ts/package.json @@ -0,0 +1,21 @@ +{ + "private": true, + "type": "module", + "scripts": { + "test": "bun test" + }, + "dependencies": { + "@earendil-works/pi-coding-agent": "^0.84.1", + "@langchain/core": "^0.3.30", + "@langchain/openai": "^0.3.16", + "@types/bun": "latest", + "@types/express": "^4.17.21", + "@types/react": "^18.3.11", + "dotenv": "^16.4.5", + "express": "^4.21.0", + "form-data": "^4.0.1", + "node-fetch": "^3.3.2", + "openai": "^4.68.0", + "react": "^18.3.1" + } +} diff --git a/tests/ts/snippets.test.ts b/tests/ts/snippets.test.ts new file mode 100644 index 0000000..1eefe53 --- /dev/null +++ b/tests/ts/snippets.test.ts @@ -0,0 +1,66 @@ +import { describe, test } from 'bun:test'; + +const generatedDir = `${import.meta.dir}/../generated_ts`; +const fixturesDir = `${generatedDir}/fixtures`; +const nodeModulesDir = `${import.meta.dir}/node_modules`; + +const glob = new Bun.Glob('*.{mts,cts,tsx,ts,mjs,cjs,jsx,js}'); +const snippetFiles = (await Array.fromAsync(glob.scan({ cwd: generatedDir }))).sort(); + +async function runSnippet(file: string) { + const childEnv: Record = { + NODE_PATH: nodeModulesDir, + PATH: process.env.PATH ?? '', + HOME: process.env.HOME ?? '', + }; + for (const key of [ + 'EDEN_AI_SANDBOX_API_TOKEN', + 'EDEN_AI_BASE_URL', + '_EDEN_TEST_FILE_ID', + ]) { + const value = process.env[key]; + if (value) childEnv[key] = value; + } + + const proc = Bun.spawn(['bun', 'run', `${generatedDir}/${file}`], { + cwd: fixturesDir, + env: childEnv, + stdout: 'pipe', + stderr: 'pipe', + }); + await proc.exited; + const stdout = await new Response(proc.stdout).text(); + const stderr = await new Response(proc.stderr).text(); + return { exitCode: proc.exitCode ?? -1, stdout, stderr }; +} + +describe('typescript documentation snippets', () => { + if (!process.env.EDEN_AI_SANDBOX_API_TOKEN) { + test.skip('EDEN_AI_SANDBOX_API_TOKEN not set', () => {}); + return; + } + + for (const file of snippetFiles) { + if (file.includes('.skip.')) { + test.skip(file, () => {}); + continue; + } + + test( + file, + async () => { + const { exitCode, stdout, stderr } = await runSnippet(file); + if (exitCode !== 0) { + throw new Error( + `snippet: ${file}\n` + + `expected: exit code 0\n` + + `actual: exit code ${exitCode}\n` + + `--- stdout ---\n${stdout || '(empty)'}\n` + + `--- stderr ---\n${stderr || '(empty)'}`, + ); + } + }, + 30_000, + ); + } +}); diff --git a/v2/index.mdx b/v2/index.mdx index 8e225e0..4e273a0 100644 --- a/v2/index.mdx +++ b/v2/index.mdx @@ -1,4 +1,4 @@ # Eden AI V2 API Documentation -Welcome to the Eden AI. The V2 documentation can be found [here](https://old-docs.edenai.co) +Welcome to the Eden AI. The V2 documentation can be found [here](https://www.edenai.co/docs/v2) diff --git a/v3/.claude/settings.local.json b/v3/.claude/settings.local.json deleted file mode 100644 index 5e87315..0000000 --- a/v3/.claude/settings.local.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "permissions": { - "allow": [ - "WebFetch(domain:api.edenai.run)", - "Bash(find /c/Users/Eden/docs/v3/_unlisted/expert-models/features -type f -name \"*.mdx\" 2>/dev/null | head -10)" - ] - } -} diff --git a/v3/expert-models/features/audio/tts.mdx b/v3/expert-models/features/audio/tts.mdx index 1560030..a97b9f1 100644 --- a/v3/expert-models/features/audio/tts.mdx +++ b/v3/expert-models/features/audio/tts.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={[`Eden AI`, `AI API`, `speech to text`, `text to speech`]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-07-27T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> ## Endpoint diff --git a/v3/expert-models/features/image/background-removal.mdx b/v3/expert-models/features/image/background-removal.mdx index e5efc19..9051a6f 100644 --- a/v3/expert-models/features/image/background-removal.mdx +++ b/v3/expert-models/features/image/background-removal.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={[`Eden AI`, `AI API`, `image analysis`, `computer vision`]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> ## Endpoint diff --git a/v3/expert-models/features/image/generation.mdx b/v3/expert-models/features/image/generation.mdx index 3846b33..446352d 100644 --- a/v3/expert-models/features/image/generation.mdx +++ b/v3/expert-models/features/image/generation.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={[`Eden AI`, `AI API`, `image analysis`, `computer vision`]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-07-27T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> ## Endpoint diff --git a/v3/expert-models/features/web/research-async.mdx b/v3/expert-models/features/web/research-async.mdx index 1732b1f..8e4465b 100644 --- a/v3/expert-models/features/web/research-async.mdx +++ b/v3/expert-models/features/web/research-async.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={[`Eden AI`, `AI API`, `web scraping`, `web search`]} datePublished="2026-07-27T00:00:00Z" - dateModified="2026-07-27T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> ## Endpoint @@ -75,7 +75,7 @@ headers = { } payload = { - "model": "web/research_async/firecrawl", + "model": "web/research_async/linkup", "input": { "prompt": "example" } @@ -90,7 +90,7 @@ curl -X POST https://api.edenai.run/v3/universal-ai/async \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ - "model": "web/research_async/firecrawl", + "model": "web/research_async/linkup", "input": {"prompt": "example"} }' ``` diff --git a/v3/expert-models/features/web/scraping.mdx b/v3/expert-models/features/web/scraping.mdx index d06ef8e..b322c81 100644 --- a/v3/expert-models/features/web/scraping.mdx +++ b/v3/expert-models/features/web/scraping.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={[`Eden AI`, `AI API`, `web scraping`, `web search`]} datePublished="2026-07-27T00:00:00Z" - dateModified="2026-07-27T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> ## Endpoint @@ -75,7 +75,7 @@ headers = { } payload = { - "model": "web/scraping/firecrawl", + "model": "web/scraping/linkup", "input": { "url": "example" } @@ -90,7 +90,7 @@ curl -X POST https://api.edenai.run/v3/universal-ai \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ - "model": "web/scraping/firecrawl", + "model": "web/scraping/linkup", "input": {"url": "example"} }' ``` diff --git a/v3/expert-models/features/web/search.mdx b/v3/expert-models/features/web/search.mdx index 9209920..63f9706 100644 --- a/v3/expert-models/features/web/search.mdx +++ b/v3/expert-models/features/web/search.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={[`Eden AI`, `AI API`, `web scraping`, `web search`]} datePublished="2026-07-27T00:00:00Z" - dateModified="2026-07-27T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> ## Endpoint @@ -86,7 +86,7 @@ headers = { } payload = { - "model": "web/search/firecrawl", + "model": "web/search/linkup", "input": { "query": "example" } @@ -101,7 +101,7 @@ curl -X POST https://api.edenai.run/v3/universal-ai \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ - "model": "web/search/firecrawl", + "model": "web/search/linkup", "input": {"query": "example"} }' ``` diff --git a/v3/integrations/claude-code.mdx b/v3/integrations/claude-code.mdx index 6c85ef2..fce3295 100644 --- a/v3/integrations/claude-code.mdx +++ b/v3/integrations/claude-code.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "Claude Code"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Configure [Claude Code](https://github.com/anthropics/claude-code), Anthropic's official CLI coding agent, to use Eden AI for access to 500+ models behind a single API key. @@ -41,7 +41,7 @@ Claude Code runs in your terminal and edits code in your project. Pointing it at ```bash bash / zsh export ANTHROPIC_API_KEY="YOUR_EDEN_AI_API_KEY" export ANTHROPIC_BASE_URL="https://api.edenai.run/v3" -export ANTHROPIC_MODEL="anthropic/claude-opus-4.7" +export ANTHROPIC_MODEL="anthropic/claude-opus-4-7" export ANTHROPIC_DEFAULT_HAIKU_MODEL="anthropic/claude-haiku-4-5" export ANTHROPIC_DEFAULT_SONNET_MODEL="anthropic/claude-sonnet-4-6" export ANTHROPIC_DEFAULT_OPUS_MODEL="anthropic/claude-opus-4-7" @@ -98,12 +98,12 @@ echo $ANTHROPIC_API_KEY curl -X POST https://api.edenai.run/v3/chat/completions \ -H "Authorization: Bearer $ANTHROPIC_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"model": "anthropic/claude-opus-4.7", "messages": [{"role": "user", "content": "ping"}]}' + -d '{"model": "anthropic/claude-opus-4-7", "messages": [{"role": "user", "content": "ping"}]}' ``` ### Model not found -Use the full `provider/model` string (e.g. `anthropic/claude-opus-4.7`, not `claude-opus-4.7`). Confirm the ID is in the catalog returned by `GET /v3/models`. +Use the full `provider/model` string (e.g. `anthropic/claude-opus-4-7`, not `claude-opus-4.7`). Confirm the ID is in the catalog returned by `GET /v3/models`. ### Connection issues diff --git a/v3/integrations/codex-cli.mdx b/v3/integrations/codex-cli.mdx index 2fce7c6..08bb57a 100644 --- a/v3/integrations/codex-cli.mdx +++ b/v3/integrations/codex-cli.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "Codex"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Configure [Codex CLI](https://github.com/openai/codex), OpenAI's open-source local coding agent, to use Eden AI for access to 500+ models behind a single API key. @@ -42,7 +42,7 @@ Codex CLI reads its provider settings from `~/.codex/config.toml`. Add an `edena ```toml ~/.codex/config.toml model_provider = "edenai" -model = "openai/gpt-5.1-codex" +model = "azure/gpt-5.1-codex" model_reasoning_effort = "high" [model_providers.edenai] @@ -78,22 +78,20 @@ Codex now sends requests to Eden AI using the model you set in `config.toml`. ## Switching models -Eden AI uses the `provider/model` format. Update the `model` field in `~/.codex/config.toml` (or pass `--model` on the command line) to switch: +Eden AI uses the `provider/model` format. Update the `model` field in `~/.codex/config.toml` (or pass `--model` on the command line) to switch. - -```toml Codex variants -model = "openai/gpt-5.1-codex" # default -model = "openai/gpt-5.1-codex-mini" # cheaper, faster -model = "openai/gpt-5.1-codex-max" # largest context -model = "openai/gpt-5.3-codex" # latest -``` +Codex variants: -```toml Other providers -model = "anthropic/claude-sonnet-4-5" -model = "google/gemini-2.5-pro" -model = "openai/gpt-4o" -``` - +- `azure/gpt-5.1-codex` (default) +- `azure/gpt-5.1-codex-mini` (cheaper, faster) +- `azure/gpt-5.1-codex-max` (largest context) +- `openai/gpt-5.3-codex` (latest) + +Other providers: + +- `anthropic/claude-sonnet-4-5` +- `google/gemini-2.5-pro` +- `openai/gpt-4o` Browse the full catalog via [List Models](/v3/llms/listing-models) or `GET https://api.edenai.run/v3/models`. @@ -109,12 +107,12 @@ echo $EDENAI_API_KEY curl -X POST https://api.edenai.run/v3/chat/completions \ -H "Authorization: Bearer $EDENAI_API_KEY" \ -H "Content-Type: application/json" \ - -d '{"model": "openai/gpt-5.1-codex", "messages": [{"role": "user", "content": "ping"}]}' + -d '{"model": "azure/gpt-5.1-codex", "messages": [{"role": "user", "content": "ping"}]}' ``` ### Model not found -Use the full `provider/model` string (e.g. `openai/gpt-5.1-codex`, not `gpt-5.1-codex`). Confirm the ID is in the catalog returned by `GET /v3/models`. +Use the full `provider/model` string (e.g. `azure/gpt-5.1-codex`, not `gpt-5.1-codex`). Confirm the ID is in the catalog returned by `GET /v3/models`. ### Connection issues diff --git a/v3/integrations/continue-dev.mdx b/v3/integrations/continue-dev.mdx index 747d6ce..e20ddac 100644 --- a/v3/integrations/continue-dev.mdx +++ b/v3/integrations/continue-dev.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "Continue.dev"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Configure Continue.dev, the open-source AI code assistant, to use Eden AI for access to 500+ models. @@ -228,6 +228,7 @@ Use slash commands for common tasks: Add codebase context for better responses: +{/* skip-test */} ```json config.json { diff --git a/v3/integrations/librechat.mdx b/v3/integrations/librechat.mdx index 1f87722..c94afe1 100644 --- a/v3/integrations/librechat.mdx +++ b/v3/integrations/librechat.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "LibreChat"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> Configure LibreChat, the open-source ChatGPT alternative, to use Eden AI for access to 500+ AI models. diff --git a/v3/integrations/open-webui.mdx b/v3/integrations/open-webui.mdx index bc464ef..8616a35 100644 --- a/v3/integrations/open-webui.mdx +++ b/v3/integrations/open-webui.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "Open WebUI"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> Configure Open WebUI (formerly Ollama WebUI) to use Eden AI for accessing 500+ AI models through a sleek interface. diff --git a/v3/integrations/openai-sdk-python.mdx b/v3/integrations/openai-sdk-python.mdx index a6094df..5496b9b 100644 --- a/v3/integrations/openai-sdk-python.mdx +++ b/v3/integrations/openai-sdk-python.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "OpenAI SDK", "Python"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> Use the official OpenAI Python SDK with Eden AI to access 500+ AI models through a familiar interface. diff --git a/v3/integrations/openai-sdk-typescript.mdx b/v3/integrations/openai-sdk-typescript.mdx index d6714d6..199f05e 100644 --- a/v3/integrations/openai-sdk-typescript.mdx +++ b/v3/integrations/openai-sdk-typescript.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "OpenAI SDK", "TypeScript"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Use the official OpenAI TypeScript/JavaScript SDK with Eden AI for seamless multi-provider access. @@ -246,9 +246,7 @@ Send images to vision models: ```typescript TypeScript import OpenAI from 'openai'; -import FormData from 'form-data'; -import fs from 'fs'; -import fetch from 'node-fetch'; +import { readFile } from 'fs/promises'; const client = new OpenAI({ apiKey: process.env.EDEN_AI_API_KEY!, @@ -257,8 +255,9 @@ const client = new OpenAI({ async function analyzeImage() { // First, upload the image + const imageBytes = await readFile('image.jpg'); const formData = new FormData(); - formData.append('file', fs.createReadStream('image.jpg')); + formData.append('file', new Blob([imageBytes]), 'image.jpg'); const uploadResponse = await fetch( 'https://api.edenai.run/v3/upload', @@ -271,7 +270,7 @@ async function analyzeImage() { } ); - const { file_id } = await uploadResponse.json(); + const { file_id } = (await uploadResponse.json()) as { file_id: string }; // Use file_id in chat const stream = await client.chat.completions.create({ @@ -352,6 +351,7 @@ safeChat(); Build a chat API with Express: +{/* skip-test */} ```typescript TypeScript import express from 'express'; diff --git a/v3/integrations/openclaw.mdx b/v3/integrations/openclaw.mdx index dd7a3ab..2d3e3f0 100644 --- a/v3/integrations/openclaw.mdx +++ b/v3/integrations/openclaw.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "OpenClaw"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-06-30T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Connect [OpenClaw](https://openclaw.ai) to Eden AI and route your agent through 500+ models with automatic fallbacks and a single API key. @@ -120,11 +120,7 @@ Every LLM call now flows through Eden AI, with built-in fallbacks, unified billi ## Using specific models -Target any model by setting the `id` to a `provider/model` string: - -```json -"id": "anthropic/claude-opus-4-7" -``` +Target any model by setting the `id` to a `provider/model` string, e.g. `"id": "anthropic/claude-opus-4-7"`. Common choices: diff --git a/v3/integrations/pi.mdx b/v3/integrations/pi.mdx index 2d092d8..6dad547 100644 --- a/v3/integrations/pi.mdx +++ b/v3/integrations/pi.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "Pi", "Flue", "Coding Agent"]} datePublished="2026-07-16T00:00:00Z" - dateModified="2026-07-16T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Use Pi with Eden AI to power your coding agent (and Flue) with 500+ AI models through one API. @@ -42,20 +42,36 @@ Register Eden AI as a custom provider from a Pi extension: ```typescript extension.ts -import { pi } from "@earendil-works/pi-coding-agent"; - -pi.registerProvider("edenai", { - name: "Eden AI", - api: "openai-completions", - baseUrl: "https://api.edenai.run/v3", - apiKey: process.env.EDEN_AI_API_KEY, - models: [ - "openai/gpt-5.5", - "openai/gpt-5-mini", - "anthropic/claude-sonnet-5", - "mistral/mistral-small-2603", - ], -}); +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; + +export default function (pi: ExtensionAPI) { + pi.registerProvider("edenai", { + name: "Eden AI", + api: "openai-completions", + baseUrl: "https://api.edenai.run/v3", + apiKey: process.env.EDEN_AI_API_KEY, + models: [ + { + id: "openai/gpt-4o", + name: "GPT-4o", + reasoning: false, + input: ["text", "image"], + cost: { input: 2.5, output: 10, cacheRead: 1.25, cacheWrite: 0 }, + contextWindow: 128000, + maxTokens: 16384, + }, + { + id: "anthropic/claude-sonnet-4-5", + name: "Claude Sonnet 4.5", + reasoning: false, + input: ["text", "image"], + cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 }, + contextWindow: 200000, + maxTokens: 8192, + }, + ], + }); +} ``` diff --git a/v3/llms.txt b/v3/llms.txt index 2e7edf9..bee0902 100644 --- a/v3/llms.txt +++ b/v3/llms.txt @@ -86,7 +86,7 @@ provider/model_name - `google/gemini-2.5-flash` - `google/gemini-2.5-pro` - `cohere/command-r-plus` -- `mistral/mistral-large` +- `mistral/mistral-large-latest` #### Message Content Types diff --git a/v3/llms/chat-completions.mdx b/v3/llms/chat-completions.mdx index 59ac840..150b91b 100644 --- a/v3/llms/chat-completions.mdx +++ b/v3/llms/chat-completions.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "LLM API", "chat completion", "OpenAI compatible"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-06-11T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> Build conversational AI applications using Eden AI's OpenAI-compatible chat completions endpoint. diff --git a/v3/llms/image-generation.mdx b/v3/llms/image-generation.mdx index 643c474..ffd2292 100644 --- a/v3/llms/image-generation.mdx +++ b/v3/llms/image-generation.mdx @@ -15,7 +15,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "image generation", "image editing", "OpenAI compatible", "gpt-image", "gemini image"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-06-11T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Generate and edit images through Eden AI's OpenAI-compatible image endpoints. Point any OpenAI client at `https://api.edenai.run/v3` and the images API works as a drop-in replacement. @@ -46,8 +46,8 @@ The image surface spans many providers. A few common model strings: | Provider | Model string | |---|---| -| Google | `google/gemini-2.5-flash-image`, `google/gemini-3-pro-image-preview`, `google/imagen-4.0-generate-001` | -| OpenAI | `openai/gpt-image-1`, `openai/gpt-image-1-mini` | +| Google | `google/gemini-3.1-flash-image`, `google/gemini-3-pro-image`, `google/imagen-4.0-generate-001` | +| OpenAI | `openai/gpt-image-2` | | Stability | `stabilityai/sd3.5-large` | | Amazon | `amazon/amazon.nova-canvas-v1:0` | @@ -66,7 +66,7 @@ headers = { } payload = { - "model": "google/gemini-2.5-flash-image", + "model": "google/gemini-3.1-flash-image", "prompt": "A nano banana dish in a fancy restaurant, cinematic lighting", "size": "1024x1024", "n": 1, @@ -85,7 +85,7 @@ curl -X POST https://api.edenai.run/v3/images/generations \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ - "model": "google/gemini-2.5-flash-image", + "model": "google/gemini-3.1-flash-image", "prompt": "A nano banana dish in a fancy restaurant, cinematic lighting", "size": "1024x1024", "n": 1 @@ -102,7 +102,7 @@ from openai import OpenAI client = OpenAI(base_url="https://api.edenai.run/v3", api_key="YOUR_API_KEY") result = client.images.generate( - model="google/gemini-2.5-flash-image", + model="google/gemini-3.1-flash-image", prompt="A nano banana dish in a fancy restaurant, cinematic lighting", size="1024x1024", n=1, @@ -115,7 +115,7 @@ print(result.data[0].url or result.data[0].b64_json[:60]) | Field | Required | Description | |---|---|---| -| `model` | yes | `provider/model`, e.g. `google/gemini-2.5-flash-image` | +| `model` | yes | `provider/model`, e.g. `google/gemini-3.1-flash-image` | | `prompt` | yes | Text description of the image to generate (1–32,000 chars) | | `n` | no | Number of images to generate (1–10) | | `size` | no | Output resolution, e.g. `1024x1024`, `1536x1024`, `auto`. Allowed values depend on the model | @@ -189,7 +189,7 @@ headers = { } payload = { - "model": "google/gemini-2.5-flash-image", + "model": "google/gemini-3.1-flash-image", "prompt": "Add a small red hat", "images": [ {"image_url": "https://example.com/photo.png"} diff --git a/v3/llms/listing-models.mdx b/v3/llms/listing-models.mdx index b8c08ae..7a2579c 100644 --- a/v3/llms/listing-models.mdx +++ b/v3/llms/listing-models.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Intermediate" keywords={["Eden AI", "AI API", "LLM API", "chat completion", "OpenAI compatible"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-07-28T00:00:00Z" + dateModified="2026-08-09T00:00:00Z" /> Use the `/v3/models` endpoint to retrieve all LLM models available through Eden AI, along with their capabilities: input modalities, reasoning, web search, and function calling. diff --git a/v3/overview/ai-gateway.mdx b/v3/overview/ai-gateway.mdx index 9fcfa1e..6815cff 100644 --- a/v3/overview/ai-gateway.mdx +++ b/v3/overview/ai-gateway.mdx @@ -14,7 +14,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Beginner" keywords={["Eden AI", "AI API", "AI gateway", "multi-provider"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Eden AI is a **unified AI gateway** that gives you access to **500+ AI models** from 50+ providers through a single API. Instead of integrating each AI provider separately, you connect once to Eden AI and get instant access to the entire AI ecosystem. @@ -159,7 +159,7 @@ console.log(result.choices[0].message.content); -If you were a user before January 2026, you still have access to the previous version at [old-app.edenai.run](https://old-app.edenai.run/). The old version will continue to be supported until the end of 2026. Documentation for the previous version is available at [old-docs.edenai.co](https://old-docs.edenai.co). +If you were a user before January 2026, you still have access to the previous version at [old-app.edenai.run](https://old-app.edenai.run/). The old version will continue to be supported until the end of 2026. Documentation for the previous version is available at [www.edenai.co/docs/v2](https://www.edenai.co/docs/v2). ## Next Steps diff --git a/v3/quickstart/first-expert-model-call.mdx b/v3/quickstart/first-expert-model-call.mdx index 6041dce..c781178 100644 --- a/v3/quickstart/first-expert-model-call.mdx +++ b/v3/quickstart/first-expert-model-call.mdx @@ -16,7 +16,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; proficiencyLevel="Beginner" keywords={["Eden AI", "AI API", "quickstart", "first API call"]} datePublished="2026-05-06T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Get started with Eden AI's Universal AI endpoint for expert models (OCR, image analysis, text processing, and more). @@ -123,8 +123,8 @@ feature/subfeature/provider[/model] | **Text** | Moderation, AI detection, NER | `text/moderation/openai` | | **OCR** | Document parsing, text extraction | `ocr/financial_parser/google` | | **Image** | Generation, detection, analysis | `image/generation/openai/dall-e-3` | -| **Audio** | Speech-to-text, text-to-speech | `audio/tts/google` | -| **Translation** | Document translation | `translation/document/google` | +| **Audio** | Speech-to-text, text-to-speech | `audio/tts/google/casual` | +| **Translation** | Document translation | `translation/document_translation/google` | Discover all expert models available through Eden AI. diff --git a/v3/quickstart/first-llm-call.mdx b/v3/quickstart/first-llm-call.mdx index fcaca3a..75b403c 100644 --- a/v3/quickstart/first-llm-call.mdx +++ b/v3/quickstart/first-llm-call.mdx @@ -24,7 +24,7 @@ import { TechArticleSchema } from "/snippets/TechArticleSchema.mdx"; "quickstart", ]} datePublished="2024-01-01T00:00:00Z" - dateModified="2026-05-07T00:00:00Z" + dateModified="2026-08-14T00:00:00Z" /> Get started with Eden AI's OpenAI-compatible LLM endpoint in minutes. @@ -132,7 +132,7 @@ Use the format `provider/model`: - `openai/gpt-4` - `anthropic/claude-sonnet-4-5` - `google/gemini-2.5-flash` -- `mistral/mistral-large` +- `mistral/mistral-large-latest` Discover all 315+ LLM models available through Eden AI.