contextpress 0.3.0__tar.gz → 0.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.
- {contextpress-0.3.0 → contextpress-0.4.0}/CHANGELOG.md +8 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/PKG-INFO +13 -2
- {contextpress-0.3.0 → contextpress-0.4.0}/README.md +12 -1
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/__init__.py +1 -1
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/core.py +53 -3
- contextpress-0.4.0/contextpress/llm/__init__.py +15 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/llm/adapters.py +41 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/pipeline.py +4 -1
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/stats.py +2 -0
- contextpress-0.4.0/contextpress/warnings_capture.py +16 -0
- contextpress-0.4.0/examples/dry_run_preview.py +23 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/pyproject.toml +1 -1
- contextpress-0.4.0/tests/test_v04.py +51 -0
- contextpress-0.3.0/contextpress/llm/__init__.py +0 -4
- {contextpress-0.3.0 → contextpress-0.4.0}/.gitignore +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/CITATION.cff +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/CONTRIBUTING.md +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/LICENSE +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/NOTICE +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/_bootstrap.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/compression.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/llm/_helpers.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/llm/base.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/models.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/normalizer.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/profiles.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/py.typed +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/registry.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/__init__.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/base.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/budget.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/filler.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/recency.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/repetition.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/contextpress/strategies/resolution.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/examples/estimate_and_stats.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/examples/llm_tier_ollama.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/examples/llm_tier_openai.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/__init__.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_budget.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_filler.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_llm_helpers.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_models.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_normalizer.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_pipeline.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_recency.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_repetition.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_resolution.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_stats.py +0 -0
- {contextpress-0.3.0 → contextpress-0.4.0}/tests/test_v03.py +0 -0
|
@@ -4,6 +4,14 @@ All notable changes to `contextpress` are recorded here.
|
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.4.0] - 2026-07-20
|
|
8
|
+
|
|
9
|
+
- **`preview()` / `dry_run=True`** — simulate compression and get stats without changing messages (Tier 1 only; no LLM calls).
|
|
10
|
+
- **`fits_budget()`** — check whether messages would fit a token budget after compression.
|
|
11
|
+
- **`CompressionStats.warnings_emitted`** — pipeline warnings collected during a run.
|
|
12
|
+
- **`OpenAICompatibleBackend`** — vLLM, LM Studio, LocalAI, and other OpenAI-compatible servers.
|
|
13
|
+
- Example: `examples/dry_run_preview.py`.
|
|
14
|
+
|
|
7
15
|
## [0.3.0] - 2026-07-14
|
|
8
16
|
|
|
9
17
|
- **`estimate_tokens(messages)`** — count tokens before compressing (same tiktoken encoding as budget).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contextpress
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Deterministic context compression for LLM chat, RAG, and agent pipelines
|
|
5
5
|
Project-URL: Homepage, https://github.com/Taha-azizi/contextpress
|
|
6
6
|
Project-URL: Documentation, https://github.com/Taha-azizi/contextpress#readme
|
|
@@ -296,6 +296,17 @@ Check token count **before** compressing:
|
|
|
296
296
|
before = cm.estimate_tokens(messages)
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
+
**Preview without changing messages** (0.4+):
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
preview = cm.preview(messages, token_budget=500)
|
|
303
|
+
print(preview.stats.tokens_saved, preview.stats.warnings_emitted)
|
|
304
|
+
assert preview.messages == messages # unchanged
|
|
305
|
+
|
|
306
|
+
if cm.fits_budget(messages, 4000):
|
|
307
|
+
...
|
|
308
|
+
```
|
|
309
|
+
|
|
299
310
|
### Custom stages (0.3+)
|
|
300
311
|
|
|
301
312
|
Register a `BaseStrategy` and include it in `stages=`:
|
|
@@ -491,7 +502,7 @@ Long chat histories inflate token usage, bury important facts (lost-in-the-middl
|
|
|
491
502
|
|
|
492
503
|
## Research and citing
|
|
493
504
|
|
|
494
|
-
For academic use, cite this package in your paper’s software or methods section. A machine-readable citation file is provided as [`CITATION.cff`](CITATION.cff)
|
|
505
|
+
For academic use, cite this package in your paper’s software or methods section. A machine-readable citation file is provided as [`CITATION.cff`](CITATION.cff).
|
|
495
506
|
|
|
496
507
|
## Extension and growth
|
|
497
508
|
|
|
@@ -56,6 +56,17 @@ Check token count **before** compressing:
|
|
|
56
56
|
before = cm.estimate_tokens(messages)
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
**Preview without changing messages** (0.4+):
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
preview = cm.preview(messages, token_budget=500)
|
|
63
|
+
print(preview.stats.tokens_saved, preview.stats.warnings_emitted)
|
|
64
|
+
assert preview.messages == messages # unchanged
|
|
65
|
+
|
|
66
|
+
if cm.fits_budget(messages, 4000):
|
|
67
|
+
...
|
|
68
|
+
```
|
|
69
|
+
|
|
59
70
|
### Custom stages (0.3+)
|
|
60
71
|
|
|
61
72
|
Register a `BaseStrategy` and include it in `stages=`:
|
|
@@ -251,7 +262,7 @@ Long chat histories inflate token usage, bury important facts (lost-in-the-middl
|
|
|
251
262
|
|
|
252
263
|
## Research and citing
|
|
253
264
|
|
|
254
|
-
For academic use, cite this package in your paper’s software or methods section. A machine-readable citation file is provided as [`CITATION.cff`](CITATION.cff)
|
|
265
|
+
For academic use, cite this package in your paper’s software or methods section. A machine-readable citation file is provided as [`CITATION.cff`](CITATION.cff).
|
|
255
266
|
|
|
256
267
|
## Extension and growth
|
|
257
268
|
|
|
@@ -6,10 +6,11 @@ from typing import TYPE_CHECKING, Any
|
|
|
6
6
|
|
|
7
7
|
from contextpress.compression import apply_stage_selection, normalize_compression_level
|
|
8
8
|
from contextpress.normalizer import denormalize_output, normalize_messages
|
|
9
|
-
from contextpress.pipeline import VALID_LLM_MODES, Pipeline
|
|
9
|
+
from contextpress.pipeline import VALID_LLM_MODES, Pipeline, clone_conversation
|
|
10
10
|
from contextpress.profiles import PROFILES, Profile, StageConfig
|
|
11
11
|
from contextpress.registry import register_stage as _register_stage
|
|
12
12
|
from contextpress.stats import CompressionResult, CompressionStats, count_conversation_tokens
|
|
13
|
+
from contextpress.warnings_capture import capture_warnings
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from collections.abc import Callable
|
|
@@ -66,6 +67,45 @@ class ContextManager:
|
|
|
66
67
|
conv, _ = normalize_messages(messages, context_type=self._type)
|
|
67
68
|
return count_conversation_tokens(conv, model if model is not None else self.model)
|
|
68
69
|
|
|
70
|
+
def fits_budget(
|
|
71
|
+
self,
|
|
72
|
+
messages: Any,
|
|
73
|
+
token_budget: int,
|
|
74
|
+
*,
|
|
75
|
+
compression: str | None = None,
|
|
76
|
+
stages: list[str] | None = None,
|
|
77
|
+
disable: list[str] | None = None,
|
|
78
|
+
) -> bool:
|
|
79
|
+
"""Return True if ``messages`` would fit ``token_budget`` after compression (dry run)."""
|
|
80
|
+
preview = self.preview(
|
|
81
|
+
messages,
|
|
82
|
+
token_budget=token_budget,
|
|
83
|
+
compression=compression,
|
|
84
|
+
stages=stages,
|
|
85
|
+
disable=disable,
|
|
86
|
+
)
|
|
87
|
+
return preview.stats.tokens_after <= token_budget
|
|
88
|
+
|
|
89
|
+
def preview(
|
|
90
|
+
self,
|
|
91
|
+
messages: Any,
|
|
92
|
+
token_budget: int | None = None,
|
|
93
|
+
*,
|
|
94
|
+
compression: str | None = None,
|
|
95
|
+
stages: list[str] | None = None,
|
|
96
|
+
disable: list[str] | None = None,
|
|
97
|
+
) -> CompressionResult:
|
|
98
|
+
"""Dry-run compression: stats only, original ``messages`` returned unchanged."""
|
|
99
|
+
return self.compress(
|
|
100
|
+
messages,
|
|
101
|
+
token_budget=token_budget,
|
|
102
|
+
compression=compression,
|
|
103
|
+
stages=stages,
|
|
104
|
+
disable=disable,
|
|
105
|
+
return_stats=True,
|
|
106
|
+
dry_run=True,
|
|
107
|
+
)
|
|
108
|
+
|
|
69
109
|
def register_stage(
|
|
70
110
|
self,
|
|
71
111
|
name: str,
|
|
@@ -87,12 +127,16 @@ class ContextManager:
|
|
|
87
127
|
stages: list[str] | None = None,
|
|
88
128
|
disable: list[str] | None = None,
|
|
89
129
|
return_stats: bool = False,
|
|
130
|
+
dry_run: bool = False,
|
|
90
131
|
) -> Any | CompressionResult:
|
|
91
132
|
"""Run the pipeline; return value matches input shape (dict list, tuples, strings, etc.).
|
|
92
133
|
|
|
93
134
|
``token_budget`` must be a positive int or None. Unknown keys in ``disable`` are ignored.
|
|
94
135
|
With ``return_stats=True``, returns a ``CompressionResult`` with ``messages`` and ``stats``.
|
|
136
|
+
With ``dry_run=True``, runs Tier 1 only (no LLM calls) and returns the original messages.
|
|
95
137
|
"""
|
|
138
|
+
if dry_run:
|
|
139
|
+
return_stats = True
|
|
96
140
|
_validate_token_budget(token_budget)
|
|
97
141
|
level = compression if compression is not None else self._compression
|
|
98
142
|
profile = copy.deepcopy(self._profile)
|
|
@@ -119,8 +163,14 @@ class ContextManager:
|
|
|
119
163
|
llm_mode=self.llm_mode,
|
|
120
164
|
custom_stages=custom_stages,
|
|
121
165
|
)
|
|
122
|
-
|
|
123
|
-
|
|
166
|
+
with capture_warnings() as captured:
|
|
167
|
+
out = pipeline.run(conv, stats=stats, dry_run=dry_run)
|
|
168
|
+
if stats is not None:
|
|
169
|
+
stats.warnings_emitted = captured
|
|
170
|
+
if dry_run:
|
|
171
|
+
messages_out = denormalize_output(clone_conversation(conv), ctx)
|
|
172
|
+
else:
|
|
173
|
+
messages_out = denormalize_output(out, ctx)
|
|
124
174
|
if return_stats:
|
|
125
175
|
assert stats is not None
|
|
126
176
|
return CompressionResult(messages=messages_out, stats=stats)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from contextpress.llm.adapters import (
|
|
2
|
+
AnthropicBackend,
|
|
3
|
+
OllamaBackend,
|
|
4
|
+
OpenAIBackend,
|
|
5
|
+
OpenAICompatibleBackend,
|
|
6
|
+
)
|
|
7
|
+
from contextpress.llm.base import LLMBackend
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"LLMBackend",
|
|
11
|
+
"OpenAIBackend",
|
|
12
|
+
"OpenAICompatibleBackend",
|
|
13
|
+
"AnthropicBackend",
|
|
14
|
+
"OllamaBackend",
|
|
15
|
+
]
|
|
@@ -221,3 +221,44 @@ class OllamaBackend(LLMBackend):
|
|
|
221
221
|
except Exception as exc:
|
|
222
222
|
warnings.warn(f"contextpress OllamaBackend.deduplicate failed: {exc}", stacklevel=2)
|
|
223
223
|
return list(range(len(turns)))
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class OpenAICompatibleBackend(OpenAIBackend):
|
|
227
|
+
"""
|
|
228
|
+
Adapter for **OpenAI-compatible HTTP APIs** (vLLM, LM Studio, LocalAI, etc.).
|
|
229
|
+
|
|
230
|
+
Requires: ``pip install openai``
|
|
231
|
+
|
|
232
|
+
Usage::
|
|
233
|
+
|
|
234
|
+
from contextpress.llm.adapters import OpenAICompatibleBackend
|
|
235
|
+
|
|
236
|
+
backend = OpenAICompatibleBackend(
|
|
237
|
+
base_url="http://localhost:8000/v1",
|
|
238
|
+
model="mistral",
|
|
239
|
+
)
|
|
240
|
+
cm = ContextManager(type="chat", llm_backend=backend)
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
def __init__(
|
|
244
|
+
self,
|
|
245
|
+
base_url: str,
|
|
246
|
+
model: str,
|
|
247
|
+
*,
|
|
248
|
+
api_key: str = "not-needed",
|
|
249
|
+
client: Any | None = None,
|
|
250
|
+
):
|
|
251
|
+
if client is not None:
|
|
252
|
+
super().__init__(client=client, model=model)
|
|
253
|
+
return
|
|
254
|
+
try:
|
|
255
|
+
from openai import OpenAI
|
|
256
|
+
except ImportError as exc:
|
|
257
|
+
raise ImportError(
|
|
258
|
+
"OpenAICompatibleBackend requires the 'openai' package. "
|
|
259
|
+
"Install with: pip install openai"
|
|
260
|
+
) from exc
|
|
261
|
+
super().__init__(
|
|
262
|
+
client=OpenAI(base_url=base_url, api_key=api_key),
|
|
263
|
+
model=model,
|
|
264
|
+
)
|
|
@@ -107,12 +107,15 @@ class Pipeline:
|
|
|
107
107
|
self,
|
|
108
108
|
conversation: Conversation,
|
|
109
109
|
stats: CompressionStats | None = None,
|
|
110
|
+
*,
|
|
111
|
+
dry_run: bool = False,
|
|
110
112
|
) -> Conversation:
|
|
111
113
|
if stats is not None:
|
|
112
114
|
stats.turns_before = len(conversation.turns)
|
|
113
115
|
stats.tokens_before = count_conversation_tokens(conversation, self.model)
|
|
114
116
|
stats.context_type = conversation.type
|
|
115
117
|
stats.token_budget = self.token_budget
|
|
118
|
+
stats.dry_run = dry_run
|
|
116
119
|
|
|
117
120
|
result = clone_conversation(conversation)
|
|
118
121
|
stage_order = effective_stage_order()
|
|
@@ -131,7 +134,7 @@ class Pipeline:
|
|
|
131
134
|
if delta != 0:
|
|
132
135
|
stats.turn_delta_by_stage[stage_name] = delta
|
|
133
136
|
|
|
134
|
-
if self.llm_backend is not None:
|
|
137
|
+
if self.llm_backend is not None and not dry_run:
|
|
135
138
|
result = self._run_llm_tier(result, stats=stats)
|
|
136
139
|
|
|
137
140
|
if stats is not None:
|
|
@@ -49,6 +49,8 @@ class CompressionStats:
|
|
|
49
49
|
compression_level: str | None = None
|
|
50
50
|
context_type: str = "chat"
|
|
51
51
|
token_budget: int | None = None
|
|
52
|
+
dry_run: bool = False
|
|
53
|
+
warnings_emitted: list[str] = field(default_factory=list)
|
|
52
54
|
|
|
53
55
|
@property
|
|
54
56
|
def turns_removed(self) -> int:
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Capture warnings emitted during compression into ``CompressionStats``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from collections.abc import Generator
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@contextmanager
|
|
11
|
+
def capture_warnings() -> Generator[list[str], None, None]:
|
|
12
|
+
captured: list[str] = []
|
|
13
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
14
|
+
warnings.simplefilter("always")
|
|
15
|
+
yield captured
|
|
16
|
+
captured.extend(str(item.message) for item in caught)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Dry-run preview: see compression stats without changing messages."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from contextpress import ContextManager
|
|
6
|
+
|
|
7
|
+
messages = [
|
|
8
|
+
{"role": "system", "content": "You are helpful."},
|
|
9
|
+
{"role": "user", "content": "Old topic: " + "blah " * 60},
|
|
10
|
+
{"role": "assistant", "content": "Basically, honestly, " + "yak " * 60},
|
|
11
|
+
{"role": "user", "content": "What is 2+2?"},
|
|
12
|
+
{"role": "assistant", "content": "Four."},
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
cm = ContextManager(type="chat", compression="medium")
|
|
16
|
+
preview = cm.preview(messages, token_budget=150)
|
|
17
|
+
|
|
18
|
+
print("dry_run:", preview.stats.dry_run)
|
|
19
|
+
print("tokens:", preview.stats.tokens_before, "->", preview.stats.tokens_after)
|
|
20
|
+
print("would fit budget 150:", cm.fits_budget(messages, 150))
|
|
21
|
+
print("messages unchanged:", preview.messages == messages)
|
|
22
|
+
if preview.stats.warnings_emitted:
|
|
23
|
+
print("warnings:", preview.stats.warnings_emitted)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from unittest.mock import MagicMock
|
|
2
|
+
|
|
3
|
+
from contextpress import ContextManager
|
|
4
|
+
from contextpress.llm.adapters import OpenAICompatibleBackend
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_preview_returns_original_messages():
|
|
8
|
+
cm = ContextManager(type="chat")
|
|
9
|
+
messages = [
|
|
10
|
+
{"role": "user", "content": "hello basically there"},
|
|
11
|
+
{"role": "assistant", "content": "hi"},
|
|
12
|
+
]
|
|
13
|
+
preview = cm.preview(messages, token_budget=500)
|
|
14
|
+
assert preview.messages == messages
|
|
15
|
+
assert preview.stats.dry_run is True
|
|
16
|
+
assert preview.stats.tokens_before >= preview.stats.tokens_after
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_dry_run_skips_llm_backend():
|
|
20
|
+
backend = MagicMock()
|
|
21
|
+
cm = ContextManager(type="chat", llm_backend=backend, llm_min_input_chars=0)
|
|
22
|
+
messages = [{"role": "user", "content": "hello " * 500}]
|
|
23
|
+
preview = cm.preview(messages, token_budget=100)
|
|
24
|
+
backend.deduplicate.assert_not_called()
|
|
25
|
+
backend.summarize.assert_not_called()
|
|
26
|
+
assert preview.stats.llm_tier_applied is False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_fits_budget():
|
|
30
|
+
cm = ContextManager(type="chat", compression="low")
|
|
31
|
+
messages = [{"role": "user", "content": "short"}]
|
|
32
|
+
assert cm.fits_budget(messages, 500) is True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_warnings_captured_on_budget_truncation():
|
|
36
|
+
cm = ContextManager(type="chat")
|
|
37
|
+
messages = [
|
|
38
|
+
{"role": "user", "content": "word " * 200},
|
|
39
|
+
{"role": "assistant", "content": "reply " * 200},
|
|
40
|
+
{"role": "user", "content": "recent"},
|
|
41
|
+
{"role": "assistant", "content": "ok"},
|
|
42
|
+
]
|
|
43
|
+
preview = cm.preview(messages, token_budget=20, compression="low")
|
|
44
|
+
assert any("token budget" in w.lower() for w in preview.stats.warnings_emitted)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_openai_compatible_backend_init():
|
|
48
|
+
client = MagicMock()
|
|
49
|
+
backend = OpenAICompatibleBackend(base_url="http://localhost:8000/v1", model="x", client=client)
|
|
50
|
+
assert backend.model == "x"
|
|
51
|
+
assert backend.client is client
|
|
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
|