mcp-token-saver 0.1.0__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.
Files changed (43) hide show
  1. mcp_token_saver-0.1.0/.gitignore +74 -0
  2. mcp_token_saver-0.1.0/CHANGELOG.md +118 -0
  3. mcp_token_saver-0.1.0/LICENSE +21 -0
  4. mcp_token_saver-0.1.0/PKG-INFO +521 -0
  5. mcp_token_saver-0.1.0/README.md +480 -0
  6. mcp_token_saver-0.1.0/pyproject.toml +81 -0
  7. mcp_token_saver-0.1.0/src/mcp_token_saver/__init__.py +50 -0
  8. mcp_token_saver-0.1.0/src/mcp_token_saver/_bedrock_retry.py +110 -0
  9. mcp_token_saver-0.1.0/src/mcp_token_saver/api.py +653 -0
  10. mcp_token_saver-0.1.0/src/mcp_token_saver/config.py +106 -0
  11. mcp_token_saver-0.1.0/src/mcp_token_saver/errors.py +36 -0
  12. mcp_token_saver-0.1.0/src/mcp_token_saver/events.py +90 -0
  13. mcp_token_saver-0.1.0/src/mcp_token_saver/pricing.py +208 -0
  14. mcp_token_saver-0.1.0/src/mcp_token_saver/protocols.py +26 -0
  15. mcp_token_saver-0.1.0/src/mcp_token_saver/providers/__init__.py +0 -0
  16. mcp_token_saver-0.1.0/src/mcp_token_saver/providers/bedrock_titan.py +57 -0
  17. mcp_token_saver-0.1.0/src/mcp_token_saver/providers/hash_provider.py +47 -0
  18. mcp_token_saver-0.1.0/src/mcp_token_saver/providers/mock.py +41 -0
  19. mcp_token_saver-0.1.0/src/mcp_token_saver/py.typed +0 -0
  20. mcp_token_saver-0.1.0/src/mcp_token_saver/router/__init__.py +1 -0
  21. mcp_token_saver-0.1.0/src/mcp_token_saver/router/base.py +52 -0
  22. mcp_token_saver-0.1.0/src/mcp_token_saver/router/embeddings_topk.py +40 -0
  23. mcp_token_saver-0.1.0/src/mcp_token_saver/router/hybrid.py +40 -0
  24. mcp_token_saver-0.1.0/src/mcp_token_saver/router/llm_router.py +143 -0
  25. mcp_token_saver-0.1.0/src/mcp_token_saver/serialization.py +33 -0
  26. mcp_token_saver-0.1.0/src/mcp_token_saver/stores/__init__.py +0 -0
  27. mcp_token_saver-0.1.0/src/mcp_token_saver/stores/faiss_inmem.py +81 -0
  28. mcp_token_saver-0.1.0/src/mcp_token_saver/stores/numpy_store.py +48 -0
  29. mcp_token_saver-0.1.0/src/mcp_token_saver/stores/pure_python.py +56 -0
  30. mcp_token_saver-0.1.0/src/mcp_token_saver/tokenizer.py +43 -0
  31. mcp_token_saver-0.1.0/tests/__init__.py +0 -0
  32. mcp_token_saver-0.1.0/tests/conftest.py +19 -0
  33. mcp_token_saver-0.1.0/tests/property/__init__.py +0 -0
  34. mcp_token_saver-0.1.0/tests/property/test_event_roundtrip.py +91 -0
  35. mcp_token_saver-0.1.0/tests/property/test_pricing_roundtrip.py +44 -0
  36. mcp_token_saver-0.1.0/tests/property/test_router_invariants.py +67 -0
  37. mcp_token_saver-0.1.0/tests/unit/__init__.py +0 -0
  38. mcp_token_saver-0.1.0/tests/unit/test_api.py +168 -0
  39. mcp_token_saver-0.1.0/tests/unit/test_config.py +54 -0
  40. mcp_token_saver-0.1.0/tests/unit/test_events.py +71 -0
  41. mcp_token_saver-0.1.0/tests/unit/test_pricing.py +63 -0
  42. mcp_token_saver-0.1.0/tests/unit/test_store_pure_python.py +51 -0
  43. mcp_token_saver-0.1.0/tests/unit/test_tokenizer.py +23 -0
@@ -0,0 +1,74 @@
1
+ # --- Python ---
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ .Python
9
+ build/
10
+ develop-eggs/
11
+ dist/
12
+ downloads/
13
+ eggs/
14
+ .eggs/
15
+ lib/
16
+ lib64/
17
+ parts/
18
+ sdist/
19
+ var/
20
+ wheels/
21
+ wheelhouse/*
22
+ !wheelhouse/.gitkeep
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # Virtual environments
30
+ .venv/
31
+ venv/
32
+ env/
33
+ ENV/
34
+ .python-version
35
+
36
+ # Testing / coverage
37
+ .pytest_cache/
38
+ .coverage
39
+ .coverage.*
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ coverage.xml
44
+ *.cover
45
+ .cache
46
+ .hypothesis/
47
+
48
+ # Type checking
49
+ .mypy_cache/
50
+ .pyre/
51
+ .pytype/
52
+ .ruff_cache/
53
+
54
+ # IDEs
55
+ .idea/
56
+ .vscode/
57
+ *.swp
58
+ *.swo
59
+
60
+ # OS
61
+ .DS_Store
62
+ Thumbs.db
63
+
64
+ # --- Docker ---
65
+ *.pid
66
+ docker-compose.override.yml
67
+ .docker/
68
+
69
+ # --- Project-local ---
70
+ .env
71
+ .env.local
72
+
73
+ # Personal dev-only scripts (not shipped)
74
+ scripts/test-install.sh
@@ -0,0 +1,118 @@
1
+ # Changelog
2
+
3
+ All notable changes to `mcp-token-saver` are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
+ the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased] — pre-PyPI hardening
9
+
10
+ ### Added
11
+
12
+ - **Async API.** `TokenSaver.afilter_tools(...)` and the module-level
13
+ `afilter_tools(...)` offload the sync routing dispatch to a worker thread via
14
+ `asyncio.to_thread`, so async gateways (FastAPI, Starlette, aiohttp) no
15
+ longer block their event loop during Haiku roundtrips.
16
+ - **`TokenSaver.aindex_catalog(...)`** async wrapper for the catalog embedding
17
+ step. Prevents the ~30-50s Titan Embed loop from blocking during FastAPI
18
+ `lifespan` startup.
19
+ - **Retry with exponential backoff for every Bedrock call.**
20
+ `_bedrock_retry.call_with_retry` wraps `invoke_model` in the Titan embedding
21
+ provider and the LLM router with jittered retries (3 attempts by default) on
22
+ transient error codes (`ThrottlingException`, `ServiceUnavailableException`,
23
+ `InternalServerException`, timeouts). Non-transient failures (validation,
24
+ auth) still fail fast.
25
+ - **`TokenSaverConfig.provider_kind`** with values `"auto"` (default),
26
+ `"bedrock"` (force Bedrock, fail loud), `"hash"` (force
27
+ `HashEmbeddingProvider`, guaranteed zero Bedrock billing). Fixes the
28
+ "accidental AWS invoice from ambient `AWS_ACCESS_KEY_ID`" footgun.
29
+ - **`TokenSaverConfig.hybrid_low_similarity_action`** with three modes:
30
+ `"haiku_shortlist"` (default), `"haiku_full"`, `"fallback_full"`. Controls
31
+ how the `hybrid` strategy handles low-confidence embeddings queries.
32
+ - **`TokenSaver(..., routing_strategy=...)`** — injection point for a custom
33
+ routing strategy. Bypasses the built-in `embeddings_topk / llm_router /
34
+ hybrid` dispatch. Also available as
35
+ `TokenSaver.default(..., routing_strategy=...)`. Docs include an OpenAI
36
+ GPT-4o-mini example.
37
+ - **`TOKEN_SAVER_STORE`** env var for the demo gateway to switch between
38
+ `numpy` (default) and `faiss` vector stores without touching code. Falls
39
+ back to numpy with a warning if the `[faiss]` extra is missing.
40
+ - **`TOKEN_SAVER_ROUTER_MODEL`** env var for the demo gateway to override the
41
+ Haiku model id (e.g. to point at a different region's cross-region inference
42
+ profile).
43
+ - **`PricingRegistry.from_dict`** and **`PricingRegistry.merge_file`** — the
44
+ former builds a fresh registry from the canonical `{"models": {...}}` shape,
45
+ the latter mutates an existing registry in place. `load_file` still returns
46
+ a fresh registry (classmethod).
47
+ - **Curated third-party pricing defaults** for OpenAI (`gpt-4o`,
48
+ `gpt-4o-mini`, `gpt-4-turbo`, `text-embedding-3-*`) and Anthropic direct API
49
+ model ids so the OpenAI custom-router example works out of the box.
50
+ - **CHANGELOG.md** (this file).
51
+
52
+ ### Changed
53
+
54
+ - **Concise INFO tracing on every routing decision.** The gateway logs one
55
+ line per `filter_tools` call and per `index_catalog` boot, with strategy,
56
+ top similarity, tokens saved, Haiku cost, and latency. Explicit
57
+ `LLM router: invoking …` lines when the LLM router is used.
58
+ - **`RoutingEvent` gets a `__post_init__`** that coerces `selected_tool_names`
59
+ from list/iterable to tuple, validates non-negative token counts, and
60
+ canonicalises float precision so JSON round-trips are lossless.
61
+ - **Default Haiku model id** now `eu.anthropic.claude-haiku-4-5-20251001-v1:0`
62
+ (previously the US-only `anthropic.claude-3-5-haiku-20241022-v1:0`, which
63
+ Bedrock rejected in EU regions with a `ValidationException`).
64
+ - **`RoutingEvent.latency_ms`** is now `float` instead of `int` — measurements
65
+ from `time.perf_counter()` were being truncated on the way in.
66
+ - **LLM router response parser hardened.** `_extract_json_array` handles raw
67
+ JSON, markdown-fenced JSON, and JSON embedded in prose so future Claude
68
+ releases that decorate output do not crash the router.
69
+ - **`README`** rewritten to document the three strategies, the pluggable
70
+ interfaces, the custom-routing example, the pricing extension mechanism,
71
+ and the demo tuning knobs.
72
+
73
+ ### Removed
74
+
75
+ - **`mcp_token_saver.events.RoutingCosts`.** Duplicate of the (correct)
76
+ `router.base.RoutingCosts`. The `events` module now exports only
77
+ `RoutingEvent` and `EventBus`.
78
+ - **`mcp_token_saver.stores.numpy_inmem.InMemoryNumpyStore`.** Despite the
79
+ name it was pure Python (no numpy), i.e. an unnecessary duplicate of
80
+ `PurePythonStore`. Removed from `__init__` exports and from the vector
81
+ stores table in `README`.
82
+ - **Langfuse integration** — the sink, the docker-compose services
83
+ (`postgres`, `clickhouse`, `redis`, `langfuse-web`, `langfuse-worker`), the
84
+ `example/langfuse/` directory, all `LANGFUSE_*` env vars, and the
85
+ `[langfuse]` install extra. The demo now ships a minimal 4-service stack
86
+ (mcp-server, gateway, agent, ui). Routing metrics live in the Streamlit UI
87
+ and the structured gateway logs.
88
+
89
+ ### Fixed
90
+
91
+ - **`hybrid` strategy no longer AttributeErrors** on low-similarity queries
92
+ (the code referenced `cfg.hybrid_low_similarity_action` but the field did
93
+ not exist in `TokenSaverConfig`).
94
+ - **`llm_router` and `hybrid` were dispatched as pure labels**, not actually
95
+ invoked. `TokenSaver.filter_tools` now truly dispatches to
96
+ `LLMRouterStrategy` / `HybridStrategy` based on `strategy_name`.
97
+ - **`RoutingCosts` import mismatch** between strategies (`events` version) and
98
+ the `RoutingStrategy` protocol (`router.base` version) was raising
99
+ `TypeError: missing 4 required positional arguments` when `llm_router` /
100
+ `hybrid` were selected. Both strategies now import from `router.base`.
101
+ - **Test suite realigned with production code.** `test_api.py` and
102
+ `test_router_invariants.py` were written against an older constructor
103
+ signature (`strategy=..., pricing_registry=...`) and referenced classes that
104
+ no longer existed. Rewritten to match the current `TokenSaver(pricing=...,
105
+ routing_strategy=...)` shape and use the deduplicated `router.base`
106
+ imports.
107
+ - **Demo gateway boot** — `mcp_server`, `mcp_gateway`, `agent`, and `ui`
108
+ `Dockerfile`s were failing on hatchling metadata due to missing
109
+ `README.md` files. Cleaned up.
110
+ - **`agent` and `ui` container CMDs** referenced modules
111
+ (`agent.main:app`, `src/ui/app.py`) that were not on the container's
112
+ `PYTHONPATH`. Package layouts fixed so `uvicorn agent.main:app` and
113
+ `streamlit run /app/src/ui/app.py` both resolve.
114
+
115
+ ## [0.1.0] — initial internal draft
116
+
117
+ - Initial semantic tool filtering skeleton, three routing strategies,
118
+ Bedrock-only demo stack, Langfuse observability sink.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 mcp-token-saver contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.