kalibr 1.2.11__tar.gz → 1.4.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 (66) hide show
  1. {kalibr-1.2.11 → kalibr-1.4.0}/PKG-INFO +15 -5
  2. {kalibr-1.2.11 → kalibr-1.4.0}/README.md +11 -0
  3. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/router.py +71 -25
  4. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/tokens.py +20 -5
  5. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr.egg-info/PKG-INFO +15 -5
  6. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr.egg-info/requires.txt +3 -1
  7. {kalibr-1.2.11 → kalibr-1.4.0}/pyproject.toml +6 -5
  8. {kalibr-1.2.11 → kalibr-1.4.0}/LICENSE +0 -0
  9. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/__init__.py +0 -0
  10. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/__main__.py +0 -0
  11. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/capsule_middleware.py +0 -0
  12. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cli/__init__.py +0 -0
  13. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cli/capsule_cmd.py +0 -0
  14. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cli/deploy_cmd.py +0 -0
  15. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cli/main.py +0 -0
  16. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cli/run.py +0 -0
  17. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cli/serve.py +0 -0
  18. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/client.py +0 -0
  19. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/collector.py +0 -0
  20. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/context.py +0 -0
  21. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/cost_adapter.py +0 -0
  22. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/decorators.py +0 -0
  23. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/instrumentation/__init__.py +0 -0
  24. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/instrumentation/anthropic_instr.py +0 -0
  25. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/instrumentation/base.py +0 -0
  26. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/instrumentation/google_instr.py +0 -0
  27. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/instrumentation/openai_instr.py +0 -0
  28. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/instrumentation/registry.py +0 -0
  29. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/intelligence.py +0 -0
  30. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/kalibr.py +0 -0
  31. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/kalibr_app.py +0 -0
  32. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/middleware/__init__.py +0 -0
  33. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/middleware/auto_tracer.py +0 -0
  34. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/models.py +0 -0
  35. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/pricing.py +0 -0
  36. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/redaction.py +0 -0
  37. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/schemas.py +0 -0
  38. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/simple_tracer.py +0 -0
  39. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/trace_capsule.py +0 -0
  40. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/trace_models.py +0 -0
  41. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/tracer.py +0 -0
  42. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/types.py +0 -0
  43. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr/utils.py +0 -0
  44. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr.egg-info/SOURCES.txt +0 -0
  45. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr.egg-info/dependency_links.txt +0 -0
  46. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr.egg-info/entry_points.txt +0 -0
  47. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr.egg-info/top_level.txt +0 -0
  48. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_crewai/__init__.py +0 -0
  49. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_crewai/callbacks.py +0 -0
  50. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_crewai/instrumentor.py +0 -0
  51. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_langchain/__init__.py +0 -0
  52. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_langchain/async_callback.py +0 -0
  53. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_langchain/callback.py +0 -0
  54. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_langchain/chat_model.py +0 -0
  55. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_openai_agents/__init__.py +0 -0
  56. {kalibr-1.2.11 → kalibr-1.4.0}/kalibr_openai_agents/processor.py +0 -0
  57. {kalibr-1.2.11 → kalibr-1.4.0}/setup.cfg +0 -0
  58. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_capsule_builder.py +0 -0
  59. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_cost_adapter.py +0 -0
  60. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_http_client_leak.py +0 -0
  61. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_instrumentation.py +0 -0
  62. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_intelligence.py +0 -0
  63. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_langchain_routing.py +0 -0
  64. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_pricing.py +0 -0
  65. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_router.py +0 -0
  66. {kalibr-1.2.11 → kalibr-1.4.0}/tests/test_thread_safety.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kalibr
3
- Version: 1.2.11
3
+ Version: 1.4.0
4
4
  Summary: Adaptive routing for AI agents. Learns which models work best and routes automatically.
5
5
  Author-email: Kalibr Team <support@kalibr.systems>
6
6
  License: Apache-2.0
@@ -13,18 +13,15 @@ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: Apache Software License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.8
17
- Classifier: Programming Language :: Python :: 3.9
18
16
  Classifier: Programming Language :: Python :: 3.10
19
17
  Classifier: Programming Language :: Python :: 3.11
20
18
  Classifier: Programming Language :: Python :: 3.12
21
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
- Requires-Python: >=3.9
21
+ Requires-Python: >=3.10
24
22
  Description-Content-Type: text/markdown
25
23
  License-File: LICENSE
26
24
  Requires-Dist: httpx>=0.27.0
27
- Requires-Dist: tiktoken>=0.8.0
28
25
  Requires-Dist: fastapi>=0.110.1
29
26
  Requires-Dist: uvicorn>=0.25.0
30
27
  Requires-Dist: pydantic>=2.6.4
@@ -35,6 +32,8 @@ Requires-Dist: requests>=2.31.0
35
32
  Requires-Dist: opentelemetry-api>=1.20.0
36
33
  Requires-Dist: opentelemetry-sdk>=1.20.0
37
34
  Requires-Dist: opentelemetry-exporter-otlp>=1.20.0
35
+ Provides-Extra: tokens
36
+ Requires-Dist: tiktoken>=0.8.0; extra == "tokens"
38
37
  Provides-Extra: langchain
39
38
  Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
40
39
  Provides-Extra: langchain-openai
@@ -73,11 +72,21 @@ Adaptive routing for AI agents. Kalibr learns which models work best for your ta
73
72
  [![Python](https://img.shields.io/pypi/pyversions/kalibr)](https://pypi.org/project/kalibr/)
74
73
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
75
74
 
75
+ ## Requirements
76
+
77
+ - Python 3.10 or higher
78
+ - pip 21.0 or higher
79
+
76
80
  ## Installation
77
81
  ```bash
78
82
  pip install kalibr
79
83
  ```
80
84
 
85
+ For accurate token counting, install with:
86
+ ```bash
87
+ pip install kalibr[tokens]
88
+ ```
89
+
81
90
  ## Setup
82
91
 
83
92
  Get your credentials from [dashboard.kalibr.systems/settings](https://dashboard.kalibr.systems/settings), then:
@@ -259,6 +268,7 @@ report_outcome(trace_id="...", goal="book_meeting", success=True)
259
268
 
260
269
  ## Other Integrations
261
270
  ```bash
271
+ pip install kalibr[tokens] # Accurate token counting (tiktoken)
262
272
  pip install kalibr[crewai] # CrewAI
263
273
  pip install kalibr[openai-agents] # OpenAI Agents SDK
264
274
  pip install kalibr[langchain-all] # LangChain with all providers
@@ -6,11 +6,21 @@ Adaptive routing for AI agents. Kalibr learns which models work best for your ta
6
6
  [![Python](https://img.shields.io/pypi/pyversions/kalibr)](https://pypi.org/project/kalibr/)
7
7
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
8
8
 
9
+ ## Requirements
10
+
11
+ - Python 3.10 or higher
12
+ - pip 21.0 or higher
13
+
9
14
  ## Installation
10
15
  ```bash
11
16
  pip install kalibr
12
17
  ```
13
18
 
19
+ For accurate token counting, install with:
20
+ ```bash
21
+ pip install kalibr[tokens]
22
+ ```
23
+
14
24
  ## Setup
15
25
 
16
26
  Get your credentials from [dashboard.kalibr.systems/settings](https://dashboard.kalibr.systems/settings), then:
@@ -192,6 +202,7 @@ report_outcome(trace_id="...", goal="book_meeting", success=True)
192
202
 
193
203
  ## Other Integrations
194
204
  ```bash
205
+ pip install kalibr[tokens] # Accurate token counting (tiktoken)
195
206
  pip install kalibr[crewai] # CrewAI
196
207
  pip install kalibr[openai-agents] # OpenAI Agents SDK
197
208
  pip install kalibr[langchain-all] # LangChain with all providers
@@ -260,34 +260,80 @@ class Router:
260
260
  else:
261
261
  router_span.set_attribute("kalibr.fallback", True)
262
262
 
263
- # Step 5: Dispatch to provider
264
- try:
265
- response = self._dispatch(model_id, messages, tool_id, **{**params, **kwargs})
266
-
267
- # Auto-report if success_when provided
268
- if self.success_when and not self._outcome_reported:
263
+ # Step 5: Build ordered candidate paths for fallback
264
+ # First: intelligence-selected path, then remaining registered paths
265
+ candidate_paths = []
266
+ selected_path = {"model": model_id, "tools": tool_id, "params": params}
267
+ candidate_paths.append(selected_path)
268
+
269
+ # Add remaining paths, skipping duplicates of the selected model
270
+ for path in self._paths:
271
+ if path["model"] != model_id:
272
+ candidate_paths.append(path)
273
+
274
+ # Step 6: Try each candidate path with fallback
275
+ from kalibr.intelligence import report_outcome
276
+
277
+ last_exception = None
278
+ for i, candidate in enumerate(candidate_paths):
279
+ candidate_model = candidate["model"]
280
+ candidate_tools = candidate.get("tools")
281
+ candidate_params = candidate.get("params") or {}
282
+
283
+ is_fallback = (i > 0)
284
+ if is_fallback:
285
+ logger.warning(f"Primary path failed, trying fallback: {candidate_model}")
286
+
287
+ try:
288
+ response = self._dispatch(
289
+ candidate_model,
290
+ messages,
291
+ candidate_tools,
292
+ **{**candidate_params, **kwargs}
293
+ )
294
+
295
+ # Success! Update state to reflect which model succeeded
296
+ self._last_model_id = candidate_model
297
+
298
+ # Auto-report success if success_when provided
299
+ if self.success_when and not self._outcome_reported:
300
+ try:
301
+ output = response.choices[0].message.content or ""
302
+ success = self.success_when(output)
303
+ self.report(success=success)
304
+ except Exception as e:
305
+ logger.warning(f"Auto-outcome evaluation failed: {e}")
306
+
307
+ # Add trace_id to response for explicit linkage
308
+ response.kalibr_trace_id = trace_id
309
+ return response
310
+
311
+ except Exception as e:
312
+ last_exception = e
313
+
314
+ # Log the failure with model name and error
315
+ logger.warning(f"Model {candidate_model} failed: {type(e).__name__}: {e}")
316
+
317
+ # Report failure for this path to enable Thompson Sampling learning
269
318
  try:
270
- output = response.choices[0].message.content or ""
271
- success = self.success_when(output)
272
- self.report(success=success)
273
- except Exception as e:
274
- logger.warning(f"Auto-outcome evaluation failed: {e}")
319
+ report_outcome(
320
+ trace_id=trace_id,
321
+ goal=self.goal,
322
+ success=False,
323
+ failure_reason=f"provider_error: {type(e).__name__}",
324
+ model_id=candidate_model,
325
+ )
326
+ except Exception:
327
+ pass
275
328
 
276
- # Add trace_id to response for explicit linkage
277
- response.kalibr_trace_id = trace_id
278
- return response
329
+ # Continue to next candidate
330
+ continue
279
331
 
280
- except Exception as e:
281
- router_span.set_attribute("error", True)
282
- router_span.set_attribute("error.type", type(e).__name__)
283
-
284
- # Auto-report failure
285
- if not self._outcome_reported:
286
- try:
287
- self.report(success=False, reason=f"provider_error: {type(e).__name__}")
288
- except:
289
- pass
290
- raise
332
+ # All paths failed - set error attributes and raise
333
+ router_span.set_attribute("error", True)
334
+ router_span.set_attribute("error.type", type(last_exception).__name__)
335
+ self._outcome_reported = True # Prevent double-reporting on raise
336
+ raise last_exception
291
337
 
292
338
  def report(
293
339
  self,
@@ -2,13 +2,18 @@
2
2
 
3
3
  from typing import Optional
4
4
 
5
- import tiktoken
5
+ # Import tiktoken optionally for token counting
6
+ try:
7
+ import tiktoken
8
+ HAS_TIKTOKEN = True
9
+ except ImportError:
10
+ HAS_TIKTOKEN = False
6
11
 
7
12
  # Cache for tokenizer instances
8
13
  _tokenizer_cache = {}
9
14
 
10
15
 
11
- def count_tokens(text: str, model_id: str) -> int:
16
+ def count_tokens(text: str, model_id: str = "gpt-4o") -> int:
12
17
  """Count tokens for given text and model.
13
18
 
14
19
  Args:
@@ -16,11 +21,15 @@ def count_tokens(text: str, model_id: str) -> int:
16
21
  model_id: Model identifier
17
22
 
18
23
  Returns:
19
- Token count (approximate)
24
+ Token count (approximate if tiktoken is not installed)
20
25
  """
21
26
  if not text:
22
27
  return 0
23
28
 
29
+ if not HAS_TIKTOKEN:
30
+ # Fallback: rough estimate of 4 chars per token
31
+ return len(text) // 4
32
+
24
33
  # Try to get exact tokenizer for OpenAI models
25
34
  if "gpt" in model_id.lower():
26
35
  try:
@@ -34,7 +43,13 @@ def count_tokens(text: str, model_id: str) -> int:
34
43
 
35
44
 
36
45
  def get_openai_encoding(model_id: str):
37
- """Get tiktoken encoding for OpenAI model."""
46
+ """Get tiktoken encoding for OpenAI model.
47
+
48
+ Returns None if tiktoken is not installed.
49
+ """
50
+ if not HAS_TIKTOKEN:
51
+ return None
52
+
38
53
  if model_id in _tokenizer_cache:
39
54
  return _tokenizer_cache[model_id]
40
55
 
@@ -48,5 +63,5 @@ def get_openai_encoding(model_id: str):
48
63
  _tokenizer_cache[model_id] = encoding
49
64
  return encoding
50
65
  except Exception as e:
51
- print(f"⚠️ Failed to load tokenizer for {model_id}: {e}")
66
+ print(f"Warning: Failed to load tokenizer for {model_id}: {e}")
52
67
  raise
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kalibr
3
- Version: 1.2.11
3
+ Version: 1.4.0
4
4
  Summary: Adaptive routing for AI agents. Learns which models work best and routes automatically.
5
5
  Author-email: Kalibr Team <support@kalibr.systems>
6
6
  License: Apache-2.0
@@ -13,18 +13,15 @@ Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: Apache Software License
15
15
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.8
17
- Classifier: Programming Language :: Python :: 3.9
18
16
  Classifier: Programming Language :: Python :: 3.10
19
17
  Classifier: Programming Language :: Python :: 3.11
20
18
  Classifier: Programming Language :: Python :: 3.12
21
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
- Requires-Python: >=3.9
21
+ Requires-Python: >=3.10
24
22
  Description-Content-Type: text/markdown
25
23
  License-File: LICENSE
26
24
  Requires-Dist: httpx>=0.27.0
27
- Requires-Dist: tiktoken>=0.8.0
28
25
  Requires-Dist: fastapi>=0.110.1
29
26
  Requires-Dist: uvicorn>=0.25.0
30
27
  Requires-Dist: pydantic>=2.6.4
@@ -35,6 +32,8 @@ Requires-Dist: requests>=2.31.0
35
32
  Requires-Dist: opentelemetry-api>=1.20.0
36
33
  Requires-Dist: opentelemetry-sdk>=1.20.0
37
34
  Requires-Dist: opentelemetry-exporter-otlp>=1.20.0
35
+ Provides-Extra: tokens
36
+ Requires-Dist: tiktoken>=0.8.0; extra == "tokens"
38
37
  Provides-Extra: langchain
39
38
  Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
40
39
  Provides-Extra: langchain-openai
@@ -73,11 +72,21 @@ Adaptive routing for AI agents. Kalibr learns which models work best for your ta
73
72
  [![Python](https://img.shields.io/pypi/pyversions/kalibr)](https://pypi.org/project/kalibr/)
74
73
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
75
74
 
75
+ ## Requirements
76
+
77
+ - Python 3.10 or higher
78
+ - pip 21.0 or higher
79
+
76
80
  ## Installation
77
81
  ```bash
78
82
  pip install kalibr
79
83
  ```
80
84
 
85
+ For accurate token counting, install with:
86
+ ```bash
87
+ pip install kalibr[tokens]
88
+ ```
89
+
81
90
  ## Setup
82
91
 
83
92
  Get your credentials from [dashboard.kalibr.systems/settings](https://dashboard.kalibr.systems/settings), then:
@@ -259,6 +268,7 @@ report_outcome(trace_id="...", goal="book_meeting", success=True)
259
268
 
260
269
  ## Other Integrations
261
270
  ```bash
271
+ pip install kalibr[tokens] # Accurate token counting (tiktoken)
262
272
  pip install kalibr[crewai] # CrewAI
263
273
  pip install kalibr[openai-agents] # OpenAI Agents SDK
264
274
  pip install kalibr[langchain-all] # LangChain with all providers
@@ -1,5 +1,4 @@
1
1
  httpx>=0.27.0
2
- tiktoken>=0.8.0
3
2
  fastapi>=0.110.1
4
3
  uvicorn>=0.25.0
5
4
  pydantic>=2.6.4
@@ -48,3 +47,6 @@ langchain-openai>=0.1.0
48
47
 
49
48
  [openai-agents]
50
49
  openai-agents>=0.0.3
50
+
51
+ [tokens]
52
+ tiktoken>=0.8.0
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kalibr"
7
- version = "1.2.11"
7
+ version = "1.4.0"
8
8
  description = "Adaptive routing for AI agents. Learns which models work best and routes automatically."
9
9
  authors = [{name = "Kalibr Team", email = "support@kalibr.systems"}]
10
10
  readme = "README.md"
11
- requires-python = ">=3.9"
11
+ requires-python = ">=3.10"
12
12
  license = {text = "Apache-2.0"}
13
13
  keywords = [
14
14
  "ai", "mcp", "gpt", "claude", "gemini", "copilot",
@@ -21,8 +21,6 @@ classifiers = [
21
21
  "Intended Audience :: Developers",
22
22
  "License :: OSI Approved :: Apache Software License",
23
23
  "Programming Language :: Python :: 3",
24
- "Programming Language :: Python :: 3.8",
25
- "Programming Language :: Python :: 3.9",
26
24
  "Programming Language :: Python :: 3.10",
27
25
  "Programming Language :: Python :: 3.11",
28
26
  "Programming Language :: Python :: 3.12",
@@ -31,7 +29,6 @@ classifiers = [
31
29
  ]
32
30
  dependencies = [
33
31
  "httpx>=0.27.0",
34
- "tiktoken>=0.8.0",
35
32
  "fastapi>=0.110.1",
36
33
  "uvicorn>=0.25.0",
37
34
  "pydantic>=2.6.4",
@@ -46,6 +43,10 @@ dependencies = [
46
43
  ]
47
44
 
48
45
  [project.optional-dependencies]
46
+ # Token counting (optional - for accurate token counts)
47
+ tokens = [
48
+ "tiktoken>=0.8.0",
49
+ ]
49
50
  # LangChain integration
50
51
  langchain = [
51
52
  "langchain-core>=0.1.0",
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