3tears-models 0.14.0__py3-none-any.whl

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 (35) hide show
  1. 3tears_models-0.14.0.dist-info/METADATA +62 -0
  2. 3tears_models-0.14.0.dist-info/RECORD +35 -0
  3. 3tears_models-0.14.0.dist-info/WHEEL +4 -0
  4. 3tears_models-0.14.0.dist-info/licenses/LICENSE +21 -0
  5. threetears/models/__init__.py +175 -0
  6. threetears/models/cache.py +99 -0
  7. threetears/models/capabilities.py +406 -0
  8. threetears/models/chunk_merging.py +70 -0
  9. threetears/models/chunk_parsing.py +120 -0
  10. threetears/models/circuit_breaker.py +378 -0
  11. threetears/models/defaults.py +68 -0
  12. threetears/models/enums.py +56 -0
  13. threetears/models/errors.py +138 -0
  14. threetears/models/factory.py +320 -0
  15. threetears/models/preprocessing.py +223 -0
  16. threetears/models/price_lookup.py +614 -0
  17. threetears/models/providers/__init__.py +8 -0
  18. threetears/models/providers/_claude_cli.py +98 -0
  19. threetears/models/providers/_voyageai_compat.py +86 -0
  20. threetears/models/providers/anthropic.py +549 -0
  21. threetears/models/providers/image/__init__.py +22 -0
  22. threetears/models/providers/image/a1111.py +119 -0
  23. threetears/models/providers/image/comfyui.py +218 -0
  24. threetears/models/providers/image/huggingface.py +94 -0
  25. threetears/models/providers/image/modelslab.py +150 -0
  26. threetears/models/providers/image/openai.py +189 -0
  27. threetears/models/providers/openai.py +339 -0
  28. threetears/models/providers/openrouter.py +550 -0
  29. threetears/models/providers/voyageai.py +109 -0
  30. threetears/models/providers/whisper.py +214 -0
  31. threetears/models/registry.py +116 -0
  32. threetears/models/registry_loader.py +210 -0
  33. threetears/models/tool_name_translation.py +311 -0
  34. threetears/models/tool_name_validation.py +186 -0
  35. threetears/models/tracking.py +896 -0
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.4
2
+ Name: 3tears-models
3
+ Version: 0.14.0
4
+ Summary: LangChain-native AI model factories with capability metadata, circuit breakers, and usage tracking for the 3tears platform
5
+ Project-URL: Repository, https://github.com/pacepace/3tears
6
+ Author: pace
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.14
10
+ Requires-Dist: 3tears-media-contracts
11
+ Requires-Dist: 3tears-observe
12
+ Requires-Dist: langchain-anthropic>=1.4
13
+ Requires-Dist: langchain-core>=0.3
14
+ Requires-Dist: langchain-openai>=1.1
15
+ Requires-Dist: langchain-openrouter>=0.1
16
+ Requires-Dist: pydantic>=2.0
17
+ Provides-Extra: claude-cli
18
+ Requires-Dist: langchain-claude-code>=0.1.0; extra == 'claude-cli'
19
+ Provides-Extra: image
20
+ Requires-Dist: httpx>=0.27; extra == 'image'
21
+ Provides-Extra: prometheus
22
+ Requires-Dist: prometheus-client>=0.20; extra == 'prometheus'
23
+ Provides-Extra: voyageai
24
+ Requires-Dist: langchain-voyageai>=0.3; extra == 'voyageai'
25
+ Provides-Extra: whisper
26
+ Requires-Dist: httpx>=0.27; extra == 'whisper'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # 3tears-models
30
+
31
+ LangChain-native AI model factories for the 3tears framework. Build chat and embedding models from a single call, with capability metadata, circuit breakers, error translation, and usage tracking wired in.
32
+
33
+ ```bash
34
+ pip install 3tears-models
35
+ ```
36
+
37
+ ## What you get
38
+
39
+ - **Factories** -- `create_chat_model()` and `create_embedding_model()` return standard LangChain `BaseChatModel` / `Embeddings` instances. No custom runtime protocols to learn.
40
+ - **Providers** -- Anthropic, OpenAI, OpenRouter, VoyageAI, Whisper, and image backends (OpenAI Images, HuggingFace, A1111, ModelsLab, ComfyUI).
41
+ - **Capability registry** -- `get_capabilities()`, `register_capabilities()`, and per-model overrides describe context windows, vision support, tool support, and tier.
42
+ - **Circuit breakers** -- `CircuitBreaker` and `CircuitBreakerRegistry` trip on repeated provider failures and recover on a timer.
43
+ - **Usage tracking** -- `UsageTracker.record()` emits an OpenTelemetry span plus Prometheus instruments, with optional per-application audit and counter sinks.
44
+ - **Message hygiene** -- `preprocess_messages()`, `enforce_alternating_roles()`, `filter_invalid_tool_calls()`, and streaming chunk helpers (`parse_chunk`, `merge_chunks`).
45
+
46
+ ## Quickstart
47
+
48
+ ```python
49
+ from threetears.models import create_chat_model, create_embedding_model
50
+
51
+ chat = create_chat_model("claude-sonnet-4-6")
52
+ reply = await chat.ainvoke("Summarize three-tier caching in one sentence.")
53
+
54
+ embedder = create_embedding_model("voyage-3")
55
+ vectors = await embedder.aembed_documents(["first doc", "second doc"])
56
+ ```
57
+
58
+ Capabilities, circuit breakers, and usage tracking attach automatically. Reach for the registry and tracker APIs directly when you need to inspect or override them.
59
+
60
+ ## License
61
+
62
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,35 @@
1
+ threetears/models/__init__.py,sha256=cQyOaVkoaBJXPo_IL_sCeiNB4cFvHinEzah-SesDyHE,5657
2
+ threetears/models/cache.py,sha256=DW4AJd1UevXfz1PdH8EMNTzBatQDWyhZw9m3RTLdC-I,3107
3
+ threetears/models/capabilities.py,sha256=J3oRJk0Nb5h6B_JaxVdoqBiGKosnAllxi17zm6KPHEI,18304
4
+ threetears/models/chunk_merging.py,sha256=xYAY3pjYzoiJJL_LXM-vf4q_fQ9v70vc533DjyCZvA8,3212
5
+ threetears/models/chunk_parsing.py,sha256=0jsb0N0wNTSeOpxONqEiKHPX8MZEPiZFCehMacVvRMg,4940
6
+ threetears/models/circuit_breaker.py,sha256=bsFOABv-NcMWgcRLEfalNZ-uBGF-XLwostE_I0HgqfU,12745
7
+ threetears/models/defaults.py,sha256=XjSNKGCes1pWcnv-QxENrG-h2V23KfnKzLypumszFcE,2985
8
+ threetears/models/enums.py,sha256=bMYt-u4TcEuxU8g5d_t-R7alJ8OV7oifI6e_9_rQSQ0,1414
9
+ threetears/models/errors.py,sha256=zbMfwLHQRqQ7ItXErAKApj8ml7q4CbNdUy7Nry8jyss,5695
10
+ threetears/models/factory.py,sha256=rD-63YsFmzrBVWh29aRUtzmUPhrIWMK0k4uPlFg4uEo,11709
11
+ threetears/models/preprocessing.py,sha256=IWt2mK6ekja8ByjO9SEDDnjf7BqUFC8_ZXyhEEIZRHc,6784
12
+ threetears/models/price_lookup.py,sha256=816Vr6HCz4eKHytOFhcpD-CxbSXLCBXGA_I5w1snDOM,26326
13
+ threetears/models/registry.py,sha256=YqLHaXKllPySTdHNP84BMpXaY7u3QuO1DsdiFLnyacs,3951
14
+ threetears/models/registry_loader.py,sha256=9tGewFaiWmElOPhOb6sb0xFCVODeBuIVaSuq4AJGAzw,8057
15
+ threetears/models/tool_name_translation.py,sha256=JQhRmHUQBo0lEDikomYyYbaXXXAaO5hDtGWypnf1tZc,12800
16
+ threetears/models/tool_name_validation.py,sha256=5ioli_7YiNO_uHyi-1Gqket3DP1EyS6uXvj4DwnELeA,8235
17
+ threetears/models/tracking.py,sha256=oz03j3f2uvXhjQDsRwBRcWHxCA7f7Bj6SGjaqjj6Fdg,36681
18
+ threetears/models/providers/__init__.py,sha256=NPwLiadG-79CDvCgQrRc7FcqAlnDTR3yw6lc5bEEGS0,361
19
+ threetears/models/providers/_claude_cli.py,sha256=GlSl19krhGoOG8wKhh9S5RH4eZnz0oWsnzl2Tn3lKOg,5158
20
+ threetears/models/providers/_voyageai_compat.py,sha256=POymh980gmoAlfzBys_QYo71x6H8n_ultkqGSOvpWt4,2936
21
+ threetears/models/providers/anthropic.py,sha256=1d1GPbVNfg16XOXdKaFpCduBpsC5rZ1vVd0ivj-Ebys,23307
22
+ threetears/models/providers/openai.py,sha256=uNYkzOj5S7kI-Hdz6a7bM3a66lWtkg2-NYQp52ANQXc,13000
23
+ threetears/models/providers/openrouter.py,sha256=S8_m86yhKpMeDut8VSlNsENceulAAWxdKfa_6mFWJw4,25158
24
+ threetears/models/providers/voyageai.py,sha256=ISTS2S9EFsl9eoJlOs2k5aDm4LD1iAqBr_Sb306dMQM,3783
25
+ threetears/models/providers/whisper.py,sha256=-SAZ7xsV2n573MhdSR_QARwJIxDilyFC9HsKuzme9JY,6428
26
+ threetears/models/providers/image/__init__.py,sha256=KD63m-c8RYPcM9izGx8EpkGgQ1gkeE2r3TEBC4AW6gY,824
27
+ threetears/models/providers/image/a1111.py,sha256=VnbsY8KhDVqBDpdtEQ16nLVN4-I9ZmBxp38pTm7KklI,3813
28
+ threetears/models/providers/image/comfyui.py,sha256=Poh-3rRYAysA_Mxv0qtyFZxk0VYJFI6jCA2SY4ImM9w,6645
29
+ threetears/models/providers/image/huggingface.py,sha256=yaLAL2Hnw9jt_m3TtnOwYTcyg4dNZl29VKQkixAwYN8,3069
30
+ threetears/models/providers/image/modelslab.py,sha256=7GZm1DYrPlsILD3MB9zCuCLUgDZYiTzEftCBDVseX7k,4866
31
+ threetears/models/providers/image/openai.py,sha256=8hU4gjcC9_aaCO6y-V6Kgw5S4MbtBpwFX71517CiKvA,5847
32
+ 3tears_models-0.14.0.dist-info/METADATA,sha256=ckSKxqk5tO7k4pTCCXfZ3_RIpLx6IIcnIIHDcBWUj2o,2816
33
+ 3tears_models-0.14.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
34
+ 3tears_models-0.14.0.dist-info/licenses/LICENSE,sha256=7GWEoEOcFJenZLt4LDzqH2K7QLxo_2m8rzG7Vv8VGXo,1066
35
+ 3tears_models-0.14.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mark Pace
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.
@@ -0,0 +1,175 @@
1
+ """LangChain-native AI model factories with capability metadata, circuit
2
+ breakers, and usage tracking.
3
+
4
+ 3tears v0.6.0+ exposes provider factory functions that return configured
5
+ LangChain ``BaseChatModel`` and ``Embeddings`` instances. The legacy
6
+ ``ChatProvider`` / ``EmbeddingProvider`` / ``TranscriptionProvider`` /
7
+ ``SpeechProvider`` / ``RerankingProvider`` runtime protocols have been
8
+ removed.
9
+
10
+ Importing this package eagerly loads the builtin provider modules so
11
+ their import-time :func:`register_capabilities` calls populate the
12
+ shared registry. This makes :func:`create_chat_model` /
13
+ :func:`create_embedding_model` work for every builtin model id without
14
+ the caller manually importing ``threetears.models.providers.<name>``.
15
+ The provider modules themselves keep ``langchain_<provider>`` imports
16
+ inside ``TYPE_CHECKING`` and inside their factory bodies, so the
17
+ eager-load only pulls capability metadata — actually instantiating a
18
+ provider model still imports its langchain backend lazily, preserving
19
+ the "install only the providers you use" property for production
20
+ callers.
21
+
22
+ v0.9.0 adds chat-model utility modules used across every consumer:
23
+ :mod:`threetears.models.chunk_merging` (merge streamed AIMessageChunks
24
+ into a single AIMessage), :mod:`threetears.models.chunk_parsing`
25
+ (extract text + reasoning per chunk), and
26
+ :mod:`threetears.models.tool_name_validation` (defend against
27
+ malformed tool names emitted by misbehaving providers). The
28
+ OpenRouter and Anthropic wrappers invoke the validator on every
29
+ response so junk tool names never reach downstream dispatch.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ from threetears.models.cache import ModelCache
35
+ from threetears.models.capabilities import (
36
+ CapabilityOverride,
37
+ ModelCapabilities,
38
+ clear_capability_overrides,
39
+ get_capabilities,
40
+ get_capability_override,
41
+ list_capabilities,
42
+ register_capabilities,
43
+ register_capability_override,
44
+ unregister_capability_override,
45
+ )
46
+ from threetears.models.defaults import (
47
+ CURRENT_ANTHROPIC_CHAT_MODELS,
48
+ CURRENT_VOYAGEAI_EMBEDDING_MODELS,
49
+ DEFAULT_CHAT_MODEL,
50
+ DEFAULT_EMBEDDING_MODEL,
51
+ DEFAULT_FAST_MODEL,
52
+ DEFAULT_LARGE_MODEL,
53
+ )
54
+ from threetears.models.chunk_merging import merge_chunks
55
+ from threetears.models.chunk_parsing import ChunkParsed, parse_chunk
56
+ from threetears.models.circuit_breaker import (
57
+ CircuitBreaker,
58
+ CircuitBreakerCallback,
59
+ CircuitBreakerRegistry,
60
+ CircuitOpenError,
61
+ CircuitState,
62
+ )
63
+ from threetears.models.enums import ModelStatus, ModelTier, ModelType
64
+ from threetears.models.errors import friendly_api_error, identify_provider
65
+ from threetears.models.factory import create_chat_model, create_embedding_model
66
+ from threetears.models.preprocessing import (
67
+ enforce_alternating_roles,
68
+ format_vision_content,
69
+ preprocess_messages,
70
+ )
71
+ from threetears.models.price_lookup import (
72
+ OPENROUTER_MODELS_URL,
73
+ VOYAGE_PRICING_URL,
74
+ ModelOption,
75
+ OpenRouterPriceSource,
76
+ PriceLookup,
77
+ PriceSource,
78
+ VoyagePriceSource,
79
+ default_price_sources,
80
+ fetch_provider_models,
81
+ list_provider_models,
82
+ lookup_price,
83
+ match_price,
84
+ register_price_source,
85
+ )
86
+ from threetears.models.registry import BUILTIN_PROVIDERS, ProviderRegistry
87
+ from threetears.models.tool_name_validation import (
88
+ ToolNameValidationError,
89
+ filter_invalid_tool_calls,
90
+ is_valid_tool_name,
91
+ validate_tool_name,
92
+ )
93
+ from threetears.models.tracking import (
94
+ LlmPurpose,
95
+ UsageAuditSink,
96
+ UsageCounterSink,
97
+ UsageRecord,
98
+ UsageTracker,
99
+ UsageTrackingCallback,
100
+ )
101
+
102
+ # Eager-import builtin provider modules so their import-time
103
+ # `register_capabilities()` calls populate the shared registry. The
104
+ # provider modules themselves do not import their respective
105
+ # `langchain_<provider>` package at module scope (those imports live
106
+ # inside TYPE_CHECKING + factory bodies) so this is metadata-only.
107
+ from threetears.models.providers import ( # noqa: E402, F401
108
+ anthropic as _anthropic_caps,
109
+ openai as _openai_caps,
110
+ openrouter as _openrouter_caps,
111
+ voyageai as _voyageai_caps,
112
+ whisper as _whisper_caps,
113
+ )
114
+
115
+ __all__ = [
116
+ "BUILTIN_PROVIDERS",
117
+ "CURRENT_ANTHROPIC_CHAT_MODELS",
118
+ "CURRENT_VOYAGEAI_EMBEDDING_MODELS",
119
+ "DEFAULT_CHAT_MODEL",
120
+ "DEFAULT_EMBEDDING_MODEL",
121
+ "DEFAULT_FAST_MODEL",
122
+ "DEFAULT_LARGE_MODEL",
123
+ "CapabilityOverride",
124
+ "ChunkParsed",
125
+ "CircuitBreaker",
126
+ "CircuitBreakerCallback",
127
+ "CircuitBreakerRegistry",
128
+ "CircuitOpenError",
129
+ "CircuitState",
130
+ "LlmPurpose",
131
+ "ModelCache",
132
+ "ModelCapabilities",
133
+ "ModelOption",
134
+ "ModelStatus",
135
+ "ModelTier",
136
+ "ModelType",
137
+ "OPENROUTER_MODELS_URL",
138
+ "OpenRouterPriceSource",
139
+ "PriceLookup",
140
+ "PriceSource",
141
+ "ProviderRegistry",
142
+ "VOYAGE_PRICING_URL",
143
+ "VoyagePriceSource",
144
+ "ToolNameValidationError",
145
+ "UsageAuditSink",
146
+ "UsageCounterSink",
147
+ "UsageRecord",
148
+ "UsageTracker",
149
+ "UsageTrackingCallback",
150
+ "clear_capability_overrides",
151
+ "create_chat_model",
152
+ "create_embedding_model",
153
+ "default_price_sources",
154
+ "enforce_alternating_roles",
155
+ "fetch_provider_models",
156
+ "filter_invalid_tool_calls",
157
+ "format_vision_content",
158
+ "friendly_api_error",
159
+ "get_capabilities",
160
+ "get_capability_override",
161
+ "identify_provider",
162
+ "is_valid_tool_name",
163
+ "list_capabilities",
164
+ "list_provider_models",
165
+ "lookup_price",
166
+ "match_price",
167
+ "merge_chunks",
168
+ "parse_chunk",
169
+ "preprocess_messages",
170
+ "register_capabilities",
171
+ "register_capability_override",
172
+ "register_price_source",
173
+ "unregister_capability_override",
174
+ "validate_tool_name",
175
+ ]
@@ -0,0 +1,99 @@
1
+ """thread-safe instance cache for model provider objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ from typing import Any
7
+
8
+ __all__ = [
9
+ "ModelCache",
10
+ ]
11
+
12
+
13
+ class ModelCache:
14
+ """thread-safe cache for model provider instances.
15
+
16
+ stores instantiated model objects keyed by (provider_name, model_name)
17
+ tuple to avoid re-instantiation on every request. supports selective
18
+ eviction for API key rotation and provider shutdown scenarios.
19
+ """
20
+
21
+ def __init__(self) -> None:
22
+ self._cache: dict[tuple[str, str], Any] = {}
23
+ self._lock: threading.Lock = threading.Lock()
24
+
25
+ def get(self, provider_name: str, model_name: str) -> Any | None:
26
+ """retrieve cached model instance.
27
+
28
+ :param provider_name: provider identifier (e.g. "anthropic")
29
+ :ptype provider_name: str
30
+ :param model_name: model identifier (e.g. "claude-sonnet-4-6")
31
+ :ptype model_name: str
32
+ :return: cached instance or None if not found
33
+ :rtype: Any | None
34
+ """
35
+ with self._lock:
36
+ result = self._cache.get((provider_name, model_name))
37
+ return result
38
+
39
+ def put(self, provider_name: str, model_name: str, instance: Any) -> None:
40
+ """store model instance in cache.
41
+
42
+ :param provider_name: provider identifier
43
+ :ptype provider_name: str
44
+ :param model_name: model identifier
45
+ :ptype model_name: str
46
+ :param instance: model instance to cache
47
+ :ptype instance: Any
48
+ """
49
+ with self._lock:
50
+ self._cache[(provider_name, model_name)] = instance
51
+
52
+ def evict(self, provider_name: str, model_name: str) -> bool:
53
+ """remove specific model instance from cache.
54
+
55
+ :param provider_name: provider identifier
56
+ :ptype provider_name: str
57
+ :param model_name: model identifier
58
+ :ptype model_name: str
59
+ :return: True if entry was found and removed, False if not found
60
+ :rtype: bool
61
+ """
62
+ with self._lock:
63
+ key = (provider_name, model_name)
64
+ if key in self._cache:
65
+ del self._cache[key]
66
+ found = True
67
+ else:
68
+ found = False
69
+ return found
70
+
71
+ def evict_provider(self, provider_name: str) -> int:
72
+ """remove all cached instances for given provider.
73
+
74
+ :param provider_name: provider identifier
75
+ :ptype provider_name: str
76
+ :return: number of entries removed
77
+ :rtype: int
78
+ """
79
+ with self._lock:
80
+ keys_to_remove = [key for key in self._cache if key[0] == provider_name]
81
+ for key in keys_to_remove:
82
+ del self._cache[key]
83
+ count = len(keys_to_remove)
84
+ return count
85
+
86
+ def clear(self) -> None:
87
+ """remove all entries from cache."""
88
+ with self._lock:
89
+ self._cache.clear()
90
+
91
+ def size(self) -> int:
92
+ """return number of cached entries.
93
+
94
+ :return: current cache size
95
+ :rtype: int
96
+ """
97
+ with self._lock:
98
+ result = len(self._cache)
99
+ return result