llmshim 0.7.1__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- llmshim-0.7.2/.github/scripts/npm-verify-served.sh +37 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.github/workflows/release.yml +18 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/Cargo.lock +1 -1
- {llmshim-0.7.1 → llmshim-0.7.2}/Cargo.toml +1 -1
- {llmshim-0.7.1 → llmshim-0.7.2}/PKG-INFO +1 -1
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/concepts/routing.md +22 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/models.md +4 -1
- {llmshim-0.7.1 → llmshim-0.7.2}/src/router.rs +43 -7
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_router.rs +60 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.github/workflows/catalog-refresh.yml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.github/workflows/pages.yml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/.gitignore +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/CLAUDE.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/CODE_OF_CONDUCT.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/CONTRIBUTING.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/LICENSE-APACHE +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/LICENSE-MIT +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/NOTICE +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/README.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/SECURITY.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/benchmarks/bench.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/benchmarks/bench_python.py +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/benchmarks/gateway_loadtest.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/benchmarks/loadtest.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/Cargo.toml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/LICENSE-APACHE +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/LICENSE-MIT +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/README.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/data/LICENSE.models.dev +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/data/README.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/data/models.dev.json +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/aliases.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/builtin.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/capabilities.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/lib.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/merge.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/parse.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/refresh.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/src/types.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/tests/catalog.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/crates/llmshim-catalog/tests/refresh.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/.gitignore +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/book.toml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/mermaid-init.js +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/mermaid.min.js +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/SUMMARY.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/concepts/contracts.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/concepts/conversations.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/concepts/portability.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/concepts/translation-flow.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/caching.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/capabilities.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/fallbacks.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/images.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/native-controls.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/reasoning.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/schemas.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/streaming.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/guides/tools.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/introduction.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/proxy/deployment.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/proxy/http-api.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/proxy/native-apis.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/proxy/scaling.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/api.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/cli.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/configuration.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/errors.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/providers.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/request-fields.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/reference/surfaces.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/start/choose.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/start/cli.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/start/clients.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/start/configure.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/start/proxy.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/docs/src/start/rust.md +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/examples/chat.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/examples/stream.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/llmshim/__init__.py +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/llmshim/_client.py +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/llmshim/_server.py +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/llmshim/types.py +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/pyproject.toml +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/breaker.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/cache.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/cli.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/client.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/config.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/cost.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/env.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/error/normalize.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/error.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/fallback.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/auth.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/distributed.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/http.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/idempotency.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/metrics.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/mod.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/gateway/quota.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/lib.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/log.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/main.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/models.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/provider.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/anthropic.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/anthropic_reasoning.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/anthropic_signature.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/chatgpt/auth.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/chatgpt/mod.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/chatgpt/streaming.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/gemini.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/mod.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/openai.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/openai_compat.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/openrouter.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/providers/xai.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/convert.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/error.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/handlers.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/health.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/mod.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/ratelimit.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/types.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/wire/mod.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/proxy/wire/receipts.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/reasoning/normalize.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/reasoning.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/schema/memo.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/schema/mod.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/schema/validate.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/schema/walk.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/shim.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/streaming.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/toolcall/streaming.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/toolcall.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/usage.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/src/vision.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/fixtures/chatgpt-red.png +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_chatgpt.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_chatgpt_proxy.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_current_models.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_fallback.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_gemini.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_gemini_tools.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_long_context.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_multimodel.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_openrouter.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_proxy.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_sglang.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_thinking.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_tool_roundtrip.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_vision.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/integration_xai.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/support/completion_status.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_advertised_models.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_anthropic.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_cache.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_chatgpt.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_cli.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_client_breaker.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_fable.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_fallback.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_fast_mode.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_gemini.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_log.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_models.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_multimodel.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_openai.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_openai_compat.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_openrouter.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_provider_contracts.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_proxy.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_proxy_convert.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_reasoning.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_reasoning_profile.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_schema.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_shim.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_signature.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_sse.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_toolcall.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_tools.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_usage.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_vision.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_wire.rs +0 -0
- {llmshim-0.7.1 → llmshim-0.7.2}/tests/unit_xai.rs +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# `npm publish` exiting 0 means the registry *accepted* the tarball, not that it
|
|
3
|
+
# serves it. 0.7.1 was accepted, logged to Sigstore, answered "may take a few
|
|
4
|
+
# minutes to become available" — and half an hour later still served nothing,
|
|
5
|
+
# behind a green job. So a publish is not done until `npm view` returns the
|
|
6
|
+
# version. Run from the package's directory, after its publish step.
|
|
7
|
+
#
|
|
8
|
+
# Bound: 20 polls, 30 s apart — about ten minutes. npm's "few minutes" has been
|
|
9
|
+
# minutes, never seconds, and a version still unserved after ten is stuck in
|
|
10
|
+
# npm's processing, which more waiting here will not fix.
|
|
11
|
+
#
|
|
12
|
+
# Compares `npm view`'s output to the version rather than its exit status: for a
|
|
13
|
+
# package that exists with a version that does not, npm's exit status has
|
|
14
|
+
# differed across releases. `--prefer-online` revalidates on every poll, so the
|
|
15
|
+
# first poll's packument — cached without the new version — cannot answer for
|
|
16
|
+
# the rest of the window.
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
ATTEMPTS=20
|
|
20
|
+
INTERVAL=30
|
|
21
|
+
NAME=$(node -p "require('./package.json').name")
|
|
22
|
+
VER=$(node -p "require('./package.json').version")
|
|
23
|
+
|
|
24
|
+
for attempt in $(seq 1 "$ATTEMPTS"); do
|
|
25
|
+
served=$(npm view --prefer-online "$NAME@$VER" version 2>/dev/null || true)
|
|
26
|
+
if [ "$served" = "$VER" ]; then
|
|
27
|
+
echo "$NAME@$VER is served by the registry."
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
echo "poll $attempt/$ATTEMPTS: $NAME@$VER not served yet"
|
|
31
|
+
if [ "$attempt" -lt "$ATTEMPTS" ]; then
|
|
32
|
+
sleep "$INTERVAL"
|
|
33
|
+
fi
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
echo "::error::$NAME@$VER was accepted by npm but is still not served after $((ATTEMPTS * INTERVAL / 60)) minutes. The publish step exited 0 and its output carries the Sigstore provenance line, so the tarball is stuck in npm's processing, not missing from this run. Look at https://www.npmjs.com/package/$NAME?activeTab=versions and https://status.npmjs.org; once it appears, re-run this job — the publish step sees it and skips. If it never appears, npm will refuse a second publish of the same version and support has to release it."
|
|
37
|
+
exit 1
|
|
@@ -223,6 +223,9 @@ jobs:
|
|
|
223
223
|
else
|
|
224
224
|
npm publish --access public --provenance
|
|
225
225
|
fi
|
|
226
|
+
- name: Verify npm serves llmshim-darwin-arm64
|
|
227
|
+
working-directory: clients/typescript/packages/llmshim-darwin-arm64
|
|
228
|
+
run: bash "$GITHUB_WORKSPACE/.github/scripts/npm-verify-served.sh"
|
|
226
229
|
- name: Publish llmshim-darwin-x64
|
|
227
230
|
working-directory: clients/typescript/packages/llmshim-darwin-x64
|
|
228
231
|
run: |
|
|
@@ -234,6 +237,9 @@ jobs:
|
|
|
234
237
|
else
|
|
235
238
|
npm publish --access public --provenance
|
|
236
239
|
fi
|
|
240
|
+
- name: Verify npm serves llmshim-darwin-x64
|
|
241
|
+
working-directory: clients/typescript/packages/llmshim-darwin-x64
|
|
242
|
+
run: bash "$GITHUB_WORKSPACE/.github/scripts/npm-verify-served.sh"
|
|
237
243
|
|
|
238
244
|
npm-linux-x64-binary:
|
|
239
245
|
needs: test
|
|
@@ -274,6 +280,9 @@ jobs:
|
|
|
274
280
|
else
|
|
275
281
|
npm publish --access public --provenance
|
|
276
282
|
fi
|
|
283
|
+
- name: Verify npm serves llmshim-linux-x64
|
|
284
|
+
working-directory: clients/typescript/packages/llmshim-linux-x64
|
|
285
|
+
run: bash "$GITHUB_WORKSPACE/.github/scripts/npm-verify-served.sh"
|
|
277
286
|
|
|
278
287
|
# Native arm64 runner (GA, free on public repos) — no cross-compilation needed.
|
|
279
288
|
npm-linux-arm64-binary:
|
|
@@ -315,6 +324,9 @@ jobs:
|
|
|
315
324
|
else
|
|
316
325
|
npm publish --access public --provenance
|
|
317
326
|
fi
|
|
327
|
+
- name: Verify npm serves llmshim-linux-arm64
|
|
328
|
+
working-directory: clients/typescript/packages/llmshim-linux-arm64
|
|
329
|
+
run: bash "$GITHUB_WORKSPACE/.github/scripts/npm-verify-served.sh"
|
|
318
330
|
|
|
319
331
|
npm-windows-binary:
|
|
320
332
|
needs: test
|
|
@@ -358,6 +370,10 @@ jobs:
|
|
|
358
370
|
else
|
|
359
371
|
npm publish --access public --provenance
|
|
360
372
|
fi
|
|
373
|
+
- name: Verify npm serves llmshim-win32-x64
|
|
374
|
+
shell: bash
|
|
375
|
+
working-directory: clients/typescript/packages/llmshim-win32-x64
|
|
376
|
+
run: bash "$GITHUB_WORKSPACE/.github/scripts/npm-verify-served.sh"
|
|
361
377
|
|
|
362
378
|
# --- Publish TypeScript client to npm ---
|
|
363
379
|
# First release must be published manually once (npm's trusted-publisher UI
|
|
@@ -410,6 +426,8 @@ jobs:
|
|
|
410
426
|
else
|
|
411
427
|
npm publish --access public --provenance
|
|
412
428
|
fi
|
|
429
|
+
- name: Verify npm serves the TypeScript client
|
|
430
|
+
run: bash "$GITHUB_WORKSPACE/.github/scripts/npm-verify-served.sh"
|
|
413
431
|
|
|
414
432
|
# --- Publish Ruby client to RubyGems ---
|
|
415
433
|
# Uses a "pending trusted publisher" — configurable on rubygems.org before
|
|
@@ -136,3 +136,25 @@ let router = llmshim::router::Router::from_env();
|
|
|
136
136
|
|
|
137
137
|
Applications that manage secrets themselves can call `Router::from_env()`
|
|
138
138
|
directly or construct a Router by registering provider implementations.
|
|
139
|
+
|
|
140
|
+
## Catalog refresh is the daemon's default, not the embedder's
|
|
141
|
+
|
|
142
|
+
`Router::from_env()` also schedules one background fetch of the model catalog
|
|
143
|
+
(prices, context windows, capabilities). That is right for the proxy, which
|
|
144
|
+
starts once and runs for days. A program that starts many times a day, or runs
|
|
145
|
+
air-gapped, should build its router with
|
|
146
|
+
`Router::from_env_without_catalog_refresh()` — identical, except that
|
|
147
|
+
constructing it makes no network call — and refresh only when it decides to:
|
|
148
|
+
|
|
149
|
+
```rust
|
|
150
|
+
let router = llmshim::router::Router::from_env_without_catalog_refresh();
|
|
151
|
+
if user_asked_for_it {
|
|
152
|
+
// Rides the caller's Tokio runtime; `None` when there is none, when
|
|
153
|
+
// LLMSHIM_CATALOG_OFFLINE=1 is set, or when the local catalog is invalid.
|
|
154
|
+
router.refresh_catalog_in_background();
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The offline router still resolves and prices every model in the vendored
|
|
159
|
+
snapshot, any earlier cached download, and the local override files; its data
|
|
160
|
+
is simply never newer than the disk.
|
|
@@ -28,7 +28,10 @@ compile-time API for curated and historical facts.
|
|
|
28
28
|
|
|
29
29
|
Startup reads the vendored floor, cached data, and local overrides without
|
|
30
30
|
waiting for network. Refresh uses ETags and a 24-hour TTL; errors keep the old
|
|
31
|
-
snapshot available.
|
|
31
|
+
snapshot available. `Router::from_env()` schedules one such refresh;
|
|
32
|
+
`Router::from_env_without_catalog_refresh()` schedules none and leaves it to
|
|
33
|
+
`Router::refresh_catalog_in_background()`. Offline mode uses only vendored and
|
|
34
|
+
local layers. User
|
|
32
35
|
overrides live in `~/.config/llmshim/models.toml`, project overrides in
|
|
33
36
|
`.llmshim/models.toml`, and cached data in `~/.cache/llmshim/models.dev.json`.
|
|
34
37
|
Local overrides win; verified builtin assertions win over models.dev data.
|
|
@@ -176,15 +176,35 @@ impl Router {
|
|
|
176
176
|
.ok_or_else(|| ShimError::UnknownProvider(key.to_string()))
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
/// Build a router from provider env vars and a saved ChatGPT login
|
|
179
|
+
/// Build a router from provider env vars and a saved ChatGPT login, and
|
|
180
|
+
/// schedule one background refresh of the model catalog.
|
|
181
|
+
///
|
|
182
|
+
/// This is the daemon's constructor. The proxy starts once and serves for
|
|
183
|
+
/// days, so a single fetch at startup is what keeps its prices and
|
|
184
|
+
/// capabilities current for the rest of its life. A process that starts
|
|
185
|
+
/// many times a day, or must run air-gapped, wants
|
|
186
|
+
/// [`Router::from_env_without_catalog_refresh`] instead and decides for
|
|
187
|
+
/// itself when — or whether — to call
|
|
188
|
+
/// [`Router::refresh_catalog_in_background`].
|
|
180
189
|
pub fn from_env() -> Self {
|
|
181
|
-
let
|
|
190
|
+
let router = Self::from_env_without_catalog_refresh();
|
|
191
|
+
router.refresh_catalog_in_background();
|
|
192
|
+
router
|
|
193
|
+
}
|
|
182
194
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
/// [`Router::from_env`] minus the catalog refresh: building a router makes
|
|
196
|
+
/// no network call.
|
|
197
|
+
///
|
|
198
|
+
/// The catalog is still there — the vendored snapshot, whatever an earlier
|
|
199
|
+
/// refresh cached on disk, and the local override files — so `resolve` and
|
|
200
|
+
/// pricing work exactly as they do after `from_env`; the data is simply
|
|
201
|
+
/// never newer than the disk. For a short-lived embedder that is the right
|
|
202
|
+
/// default: a CLI that phones home for a price list before the user has
|
|
203
|
+
/// typed anything is doing something nobody asked for, and a machine with
|
|
204
|
+
/// no route out should not have to discover `LLMSHIM_CATALOG_OFFLINE` to
|
|
205
|
+
/// stop it. The fetch becomes something the caller asks for.
|
|
206
|
+
pub fn from_env_without_catalog_refresh() -> Self {
|
|
207
|
+
let mut router = Router::new();
|
|
188
208
|
|
|
189
209
|
// Named routes are configuration, not discovery: they come from
|
|
190
210
|
// ~/.llmshim/config.toml and nothing synthesizes a default set.
|
|
@@ -230,6 +250,22 @@ impl Router {
|
|
|
230
250
|
router
|
|
231
251
|
}
|
|
232
252
|
|
|
253
|
+
/// Refresh the shared model catalog from the network, detached from every
|
|
254
|
+
/// request: only startup's local snapshot is synchronous, and no catalog
|
|
255
|
+
/// HTTP fetch is ever awaited by a model request.
|
|
256
|
+
///
|
|
257
|
+
/// The refresh rides the caller's Tokio runtime and never creates one, so
|
|
258
|
+
/// `None` means nothing was scheduled: there is no runtime on this thread,
|
|
259
|
+
/// `LLMSHIM_CATALOG_OFFLINE=1` is set, or the local catalog configuration
|
|
260
|
+
/// is invalid. The catalog is process-wide — it is what every router in
|
|
261
|
+
/// the process resolves against — so refreshing through one router
|
|
262
|
+
/// refreshes it for all of them.
|
|
263
|
+
pub fn refresh_catalog_in_background(
|
|
264
|
+
&self,
|
|
265
|
+
) -> Option<tokio::task::JoinHandle<crate::catalog::RefreshOutcome>> {
|
|
266
|
+
crate::catalog::global().ok()?.refresh_in_background()
|
|
267
|
+
}
|
|
268
|
+
|
|
233
269
|
/// Resolve model string to (provider, model_name).
|
|
234
270
|
pub fn resolve(&self, model: &str) -> Result<(&dyn Provider, String)> {
|
|
235
271
|
let (key, model) = self.resolve_key(model)?;
|
|
@@ -432,3 +432,63 @@ fn routes_are_configuration_and_parse_from_the_config_file() {
|
|
|
432
432
|
"the target is not a setting"
|
|
433
433
|
);
|
|
434
434
|
}
|
|
435
|
+
|
|
436
|
+
// ============================================================
|
|
437
|
+
// from_env / from_env_without_catalog_refresh
|
|
438
|
+
// ============================================================
|
|
439
|
+
|
|
440
|
+
fn alive_tasks() -> usize {
|
|
441
|
+
tokio::runtime::Handle::current()
|
|
442
|
+
.metrics()
|
|
443
|
+
.num_alive_tasks()
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
fn catalog_offline() -> bool {
|
|
447
|
+
std::env::var("LLMSHIM_CATALOG_OFFLINE").is_ok_and(|v| v == "1")
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/// The refresh's one observable is the task it spawns. On a current-thread
|
|
451
|
+
/// runtime that task cannot run until this test yields, and the test never
|
|
452
|
+
/// does, so the fetch never starts even without `LLMSHIM_CATALOG_OFFLINE`.
|
|
453
|
+
///
|
|
454
|
+
/// Under CI's `LLMSHIM_CATALOG_OFFLINE=1` neither constructor may spawn, so
|
|
455
|
+
/// only the first assertion discriminates there; the `from_env` half is live
|
|
456
|
+
/// when a developer runs the tests without the variable.
|
|
457
|
+
#[tokio::test(flavor = "current_thread")]
|
|
458
|
+
async fn offline_constructor_spawns_no_refresh_and_from_env_still_does() {
|
|
459
|
+
let before = alive_tasks();
|
|
460
|
+
let router = Router::from_env_without_catalog_refresh();
|
|
461
|
+
assert_eq!(
|
|
462
|
+
alive_tasks(),
|
|
463
|
+
before,
|
|
464
|
+
"constructing the router spawned a task"
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
let expected = usize::from(!catalog_offline());
|
|
468
|
+
let scheduled = router.refresh_catalog_in_background();
|
|
469
|
+
assert_eq!(scheduled.is_some(), !catalog_offline());
|
|
470
|
+
assert_eq!(alive_tasks(), before + expected);
|
|
471
|
+
if let Some(task) = scheduled {
|
|
472
|
+
task.abort();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
let before = alive_tasks();
|
|
476
|
+
let _ = Router::from_env();
|
|
477
|
+
assert_eq!(
|
|
478
|
+
alive_tasks(),
|
|
479
|
+
before + expected,
|
|
480
|
+
"from_env no longer refreshes"
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/// No refresh still means a catalog: the vendored snapshot resolves without
|
|
485
|
+
/// any network having happened.
|
|
486
|
+
#[test]
|
|
487
|
+
fn offline_constructor_resolves_a_bundled_model() {
|
|
488
|
+
let router = Router::from_env_without_catalog_refresh()
|
|
489
|
+
.register("anthropic", Box::new(Anthropic::new("test".into())));
|
|
490
|
+
let (provider, model) = router.resolve("anthropic/claude-sonnet-5").unwrap();
|
|
491
|
+
assert_eq!(provider.name(), "anthropic");
|
|
492
|
+
assert_eq!(model, "claude-sonnet-5");
|
|
493
|
+
assert!(llmshim::catalog::resolve("anthropic/claude-sonnet-5").is_some());
|
|
494
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|