latencyops 0.1.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.
latencyops/__init__.py ADDED
@@ -0,0 +1,33 @@
1
+ """LatencyOps: latency measurement and quality-aware inference planning."""
2
+
3
+ from .capabilities import EnforcedPlan, ProviderCapabilities, enforce_plan
4
+ from .controller import ProactiveController
5
+ from .gateway import GatewayService, create_server
6
+ from .models import LatencyContract, RequestProfile, StreamChunk, StreamingResult
7
+ from .policy import AdaptiveProviderSelector, ElasticInferencePlanner
8
+ from .orchestrators import PrometheusMetricsCollector, SGLangMetricsCollector, TensorRTLLMMetricsCollector, VLLMMetricsCollector
9
+ from .signals import RuntimeSignalNormalizer
10
+ from .scheduling import CachePressureSignal, InMemoryRequestQueue, PriorityScheduler
11
+
12
+ __all__ = [
13
+ "AdaptiveProviderSelector",
14
+ "CachePressureSignal",
15
+ "ElasticInferencePlanner",
16
+ "EnforcedPlan",
17
+ "GatewayService",
18
+ "InMemoryRequestQueue",
19
+ "LatencyContract",
20
+ "PriorityScheduler",
21
+ "PrometheusMetricsCollector",
22
+ "ProactiveController",
23
+ "ProviderCapabilities",
24
+ "RequestProfile",
25
+ "RuntimeSignalNormalizer",
26
+ "SGLangMetricsCollector",
27
+ "TensorRTLLMMetricsCollector",
28
+ "VLLMMetricsCollector",
29
+ "StreamChunk",
30
+ "StreamingResult",
31
+ "create_server",
32
+ "enforce_plan",
33
+ ]
latencyops/adapters.py ADDED
@@ -0,0 +1,186 @@
1
+ """Provider adapters built on the standard library HTTP client."""
2
+
3
+ import json
4
+ from dataclasses import dataclass
5
+ from typing import Callable, Iterable, Mapping
6
+ from urllib.error import HTTPError, URLError
7
+ from urllib.request import Request, urlopen
8
+
9
+ from .capabilities import ProviderCapabilities
10
+ from .models import InferencePlan, StreamChunk
11
+
12
+
13
+ class ProviderError(RuntimeError):
14
+ """Normalized provider failure without including request content."""
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class ProviderHealth:
19
+ """Result of a provider health probe."""
20
+
21
+ healthy: bool
22
+ detail: str
23
+
24
+
25
+ class CallableProvider:
26
+ """Wrap a local callable as a deterministic inference provider."""
27
+
28
+ def __init__(self, name: str, responder: Callable[[str, InferencePlan], str]):
29
+ self.name = name
30
+ self.responder = responder
31
+
32
+ def generate(self, prompt: str, plan: InferencePlan) -> str:
33
+ return self.responder(prompt, plan)
34
+
35
+ def stream(self, prompt: str, plan: InferencePlan) -> Iterable[StreamChunk]:
36
+ yield StreamChunk(self.generate(prompt, plan))
37
+
38
+
39
+ class OpenAICompatibleProvider:
40
+ """Minimal adapter for JSON-compatible OpenAI-style completion endpoints."""
41
+
42
+ def __init__(
43
+ self,
44
+ name: str,
45
+ endpoint: str,
46
+ api_key: str | None = None,
47
+ timeout: float = 30.0,
48
+ model_names: Mapping[str, str] | None = None,
49
+ health_endpoint: str | None = None,
50
+ ):
51
+ self.name = name
52
+ self.endpoint = endpoint
53
+ self.api_key = api_key
54
+ self.timeout = timeout
55
+ self.model_names = dict(model_names or {})
56
+ self.health_endpoint = health_endpoint
57
+ self.capabilities = ProviderCapabilities()
58
+
59
+ def _model_name(self, plan: InferencePlan) -> str:
60
+ return self.model_names.get(plan.model_tier, plan.model_tier)
61
+
62
+ def _headers(self, streaming: bool = False) -> dict[str, str]:
63
+ headers = {"Content-Type": "application/json"}
64
+ if streaming:
65
+ headers["Accept"] = "text/event-stream"
66
+ if self.api_key:
67
+ headers["Authorization"] = f"Bearer {self.api_key}"
68
+ return headers
69
+
70
+ def generate(self, prompt: str, plan: InferencePlan) -> str:
71
+ payload = json.dumps({"model": self._model_name(plan), "prompt": prompt, "stream": False}).encode()
72
+ request = Request(self.endpoint, data=payload, headers=self._headers(), method="POST")
73
+ try:
74
+ with urlopen(request, timeout=self.timeout) as response:
75
+ body = json.loads(response.read().decode())
76
+ except (HTTPError, URLError, TimeoutError, ConnectionError, json.JSONDecodeError) as exc:
77
+ raise ProviderError(f"provider {self.name!r} request failed") from exc
78
+ choices = body.get("choices", [])
79
+ if not choices or "text" not in choices[0]:
80
+ raise ProviderError(f"provider {self.name!r} returned an invalid response")
81
+ return choices[0]["text"]
82
+
83
+ def stream(self, prompt: str, plan: InferencePlan) -> Iterable[StreamChunk]:
84
+ payload = json.dumps({"model": self._model_name(plan), "prompt": prompt, "stream": True}).encode()
85
+ request = Request(self.endpoint, data=payload, headers=self._headers(streaming=True), method="POST")
86
+ try:
87
+ with urlopen(request, timeout=self.timeout) as response:
88
+ for raw_line in response:
89
+ line = raw_line.decode().strip()
90
+ if not line or not line.startswith("data:"):
91
+ continue
92
+ data = line[5:].strip()
93
+ if data == "[DONE]":
94
+ break
95
+ event = json.loads(data)
96
+ text = event.get("choices", [{}])[0].get("text", "")
97
+ if text:
98
+ yield StreamChunk(text)
99
+ except (HTTPError, URLError, TimeoutError, ConnectionError, json.JSONDecodeError) as exc:
100
+ raise ProviderError(f"provider {self.name!r} stream failed") from exc
101
+
102
+ def health(self) -> ProviderHealth:
103
+ if not self.health_endpoint:
104
+ return ProviderHealth(True, "health probe not configured")
105
+ request = Request(self.health_endpoint, headers=self._headers(), method="GET")
106
+ try:
107
+ with urlopen(request, timeout=self.timeout) as response:
108
+ return ProviderHealth(200 <= response.status < 300, "HTTP health probe completed")
109
+ except (HTTPError, URLError, TimeoutError, ConnectionError) as exc:
110
+ return ProviderHealth(False, "HTTP health probe failed")
111
+
112
+
113
+ class OpenAIChatCompatibleProvider(OpenAICompatibleProvider):
114
+ """Adapter for OpenAI-compatible chat-completion endpoints."""
115
+
116
+ def __init__(self, *args, chat_template_kwargs: Mapping[str, object] | None = None, **kwargs):
117
+ super().__init__(*args, **kwargs)
118
+ self.chat_template_kwargs = dict(chat_template_kwargs or {})
119
+
120
+ def _payload(self, prompt: str, plan: InferencePlan, streaming: bool) -> dict[str, object]:
121
+ payload: dict[str, object] = {
122
+ "model": self._model_name(plan),
123
+ "messages": [{"role": "user", "content": prompt}],
124
+ "stream": streaming,
125
+ }
126
+ if self.chat_template_kwargs:
127
+ payload["chat_template_kwargs"] = self.chat_template_kwargs
128
+ return payload
129
+
130
+ def generate(self, prompt: str, plan: InferencePlan) -> str:
131
+ request = Request(
132
+ self.endpoint,
133
+ data=json.dumps(self._payload(prompt, plan, False)).encode(),
134
+ headers=self._headers(),
135
+ method="POST",
136
+ )
137
+ try:
138
+ with urlopen(request, timeout=self.timeout) as response:
139
+ body = json.loads(response.read().decode())
140
+ except (HTTPError, URLError, TimeoutError, ConnectionError, json.JSONDecodeError) as exc:
141
+ raise ProviderError(f"provider {self.name!r} request failed") from exc
142
+ choices = body.get("choices", [])
143
+ content = choices[0].get("message", {}).get("content") if choices else None
144
+ if not isinstance(content, str):
145
+ raise ProviderError(f"provider {self.name!r} returned an invalid chat response")
146
+ return content
147
+
148
+ def stream(self, prompt: str, plan: InferencePlan) -> Iterable[StreamChunk]:
149
+ request = Request(
150
+ self.endpoint,
151
+ data=json.dumps(self._payload(prompt, plan, True)).encode(),
152
+ headers=self._headers(streaming=True),
153
+ method="POST",
154
+ )
155
+ try:
156
+ with urlopen(request, timeout=self.timeout) as response:
157
+ for raw_line in response:
158
+ line = raw_line.decode().strip()
159
+ if not line or not line.startswith("data:"):
160
+ continue
161
+ data = line[5:].strip()
162
+ if data == "[DONE]":
163
+ break
164
+ event = json.loads(data)
165
+ choice = event.get("choices", [{}])[0]
166
+ delta = choice.get("delta", {})
167
+ text = delta.get("content", "")
168
+ if text:
169
+ yield StreamChunk(text)
170
+ except (HTTPError, URLError, TimeoutError, ConnectionError, json.JSONDecodeError) as exc:
171
+ raise ProviderError(f"provider {self.name!r} chat stream failed") from exc
172
+
173
+ def list_models(self) -> list[str]:
174
+ """Return model IDs visible to this provider credential."""
175
+ endpoint = self.endpoint.rsplit("/v1/", 1)[0] + "/v1/models"
176
+ request = Request(endpoint, headers=self._headers(), method="GET")
177
+ try:
178
+ with urlopen(request, timeout=self.timeout) as response:
179
+ body = json.loads(response.read().decode())
180
+ except (HTTPError, URLError, TimeoutError, ConnectionError, json.JSONDecodeError) as exc:
181
+ raise ProviderError(f"provider {self.name!r} model listing failed") from exc
182
+ return [item["id"] for item in body.get("data", []) if isinstance(item.get("id"), str)]
183
+
184
+
185
+ class VLLMCompatibleProvider(OpenAIChatCompatibleProvider):
186
+ """vLLM adapter using its OpenAI-compatible chat-completions endpoint."""
@@ -0,0 +1,64 @@
1
+ """Small benchmark runner for provider comparisons."""
2
+
3
+ from time import perf_counter
4
+ from typing import Callable, Iterable
5
+
6
+ from .models import InferencePlan, LatencySample, StreamingResult
7
+ from .router import InferenceProvider, StreamingProvider
8
+
9
+
10
+ class BenchmarkRunner:
11
+ """Run synthetic or approved prompts without retaining prompt content."""
12
+
13
+ def __init__(self, clock: Callable[[], float] = perf_counter):
14
+ self.clock = clock
15
+
16
+ def run(self, provider: InferenceProvider, prompts: Iterable[str], plan: InferencePlan) -> list[LatencySample]:
17
+ samples: list[LatencySample] = []
18
+ for prompt in prompts:
19
+ started = self.clock()
20
+ try:
21
+ provider.generate(prompt, plan)
22
+ except Exception:
23
+ elapsed_ms = (self.clock() - started) * 1000
24
+ samples.append(LatencySample(elapsed_ms, None, elapsed_ms, success=False))
25
+ continue
26
+ elapsed_ms = (self.clock() - started) * 1000
27
+ samples.append(LatencySample(elapsed_ms, None, elapsed_ms))
28
+ return samples
29
+
30
+ def run_streaming(
31
+ self, provider: StreamingProvider, prompt: str, plan: InferencePlan
32
+ ) -> StreamingResult:
33
+ """Collect one stream and measure TTFT and mean TPOT separately."""
34
+ started = self.clock()
35
+ first_chunk_at: float | None = None
36
+ first_chunk_token_count = 0
37
+ token_count = 0
38
+ text: list[str] = []
39
+ try:
40
+ for chunk in provider.stream(prompt, plan):
41
+ now = self.clock()
42
+ if first_chunk_at is None:
43
+ first_chunk_at = now
44
+ first_chunk_token_count = chunk.token_count
45
+ token_count += chunk.token_count
46
+ text.append(chunk.text)
47
+ except Exception:
48
+ elapsed_ms = (self.clock() - started) * 1000
49
+ return StreamingResult("".join(text), LatencySample(
50
+ (first_chunk_at - started) * 1000 if first_chunk_at is not None else elapsed_ms,
51
+ None,
52
+ elapsed_ms,
53
+ success=False,
54
+ ))
55
+
56
+ ended = self.clock()
57
+ elapsed_ms = (ended - started) * 1000
58
+ if first_chunk_at is None:
59
+ return StreamingResult("", LatencySample(elapsed_ms, None, elapsed_ms))
60
+ intervals = max(token_count - first_chunk_token_count, 0)
61
+ tpot_ms = ((ended - first_chunk_at) * 1000 / intervals) if intervals else None
62
+ return StreamingResult("".join(text), LatencySample(
63
+ (first_chunk_at - started) * 1000, tpot_ms, elapsed_ms
64
+ ))
@@ -0,0 +1,112 @@
1
+ """Scenario benchmark runner for proactive policy evaluation."""
2
+
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from dataclasses import dataclass
5
+ from time import sleep
6
+ from typing import Callable
7
+
8
+ from .benchmark import BenchmarkRunner
9
+ from .models import InferencePlan, LatencyContract, LatencySample, RequestProfile
10
+ from .router import StreamingProvider
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class BenchmarkScenario:
15
+ """A repeatable workload definition with content-free reporting metadata."""
16
+
17
+ name: str
18
+ prompt: str
19
+ contract: LatencyContract
20
+ profile: RequestProfile
21
+ cache_state: str = "unknown"
22
+ network_region: str = "unknown"
23
+ estimated_cost_per_1k_tokens: float | None = None
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class BenchmarkConfig:
28
+ """Execution controls for warm-up, concurrency, and optional rate limiting."""
29
+
30
+ warmup_runs: int = 0
31
+ measured_runs: int = 1
32
+ concurrency: int = 1
33
+ requests_per_second: float | None = None
34
+
35
+ def __post_init__(self) -> None:
36
+ if min(self.warmup_runs, self.measured_runs) < 0:
37
+ raise ValueError("run counts cannot be negative")
38
+ if self.concurrency < 1:
39
+ raise ValueError("concurrency must be positive")
40
+ if self.requests_per_second is not None and self.requests_per_second <= 0:
41
+ raise ValueError("requests_per_second must be positive")
42
+
43
+
44
+ class ScenarioBenchmarkRunner:
45
+ """Run identical scenarios concurrently and report contract outcomes."""
46
+
47
+ def __init__(self, clock_runner: BenchmarkRunner | None = None):
48
+ self.runner = clock_runner or BenchmarkRunner()
49
+
50
+ def run(
51
+ self,
52
+ provider: StreamingProvider,
53
+ scenario: BenchmarkScenario,
54
+ plan: InferencePlan,
55
+ config: BenchmarkConfig | None = None,
56
+ quality_evaluator: Callable[[str], float] | None = None,
57
+ ) -> dict[str, object]:
58
+ config = config or BenchmarkConfig()
59
+ for _ in range(config.warmup_runs):
60
+ self.runner.run_streaming(provider, scenario.prompt, plan)
61
+ interval = 1 / config.requests_per_second if config.requests_per_second else 0
62
+
63
+ def measure(_: int) -> tuple[LatencySample, float | None]:
64
+ if interval:
65
+ sleep(interval)
66
+ result = self.runner.run_streaming(provider, scenario.prompt, plan)
67
+ quality = quality_evaluator(result.text) if quality_evaluator else None
68
+ return result.sample, quality
69
+
70
+ with ThreadPoolExecutor(max_workers=config.concurrency) as executor:
71
+ measured = list(executor.map(measure, range(config.measured_runs)))
72
+ samples = [item[0] for item in measured]
73
+ summary = self._summary(samples, scenario, measured)
74
+ return summary
75
+
76
+ @staticmethod
77
+ def _summary(
78
+ samples: list[LatencySample],
79
+ scenario: BenchmarkScenario,
80
+ measured: list[tuple[LatencySample, float | None]],
81
+ ) -> dict[str, object]:
82
+ successful = [sample for sample in samples if sample.success]
83
+ quality_scores = [score for _, score in measured if score is not None]
84
+ deadline_violations = sum(
85
+ sample.success and sample.end_to_end_ms > scenario.contract.deadline_ms
86
+ for sample in samples
87
+ )
88
+ tokens = scenario.profile.prompt_tokens + scenario.profile.expected_output_tokens
89
+ cost = (
90
+ tokens / 1000 * scenario.estimated_cost_per_1k_tokens
91
+ if scenario.estimated_cost_per_1k_tokens is not None else None
92
+ )
93
+ from .metrics import percentile
94
+ return {
95
+ "scenario": scenario.name,
96
+ "runs": len(samples),
97
+ "warmup_excluded": True,
98
+ "concurrency": len(samples),
99
+ "success_rate": len(successful) / len(samples) if samples else 0.0,
100
+ "ttft_p50_ms": percentile([s.ttft_ms for s in samples], 50) if samples else None,
101
+ "ttft_p95_ms": percentile([s.ttft_ms for s in samples], 95) if samples else None,
102
+ "tpot_p50_ms": percentile([s.tpot_ms for s in successful if s.tpot_ms is not None], 50) if any(s.tpot_ms is not None for s in successful) else None,
103
+ "e2e_p50_ms": percentile([s.end_to_end_ms for s in samples], 50) if samples else None,
104
+ "e2e_p95_ms": percentile([s.end_to_end_ms for s in samples], 95) if samples else None,
105
+ "deadline_violations": deadline_violations,
106
+ "quality_mean": sum(quality_scores) / len(quality_scores) if quality_scores else None,
107
+ "quality_floor": scenario.contract.quality_floor,
108
+ "quality_satisfied": all(score >= scenario.contract.quality_floor for score in quality_scores) if quality_scores else None,
109
+ "estimated_cost": cost,
110
+ "cache_state": scenario.cache_state,
111
+ "network_region": scenario.network_region,
112
+ }
@@ -0,0 +1,66 @@
1
+ """Provider capability negotiation and safe plan enforcement."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .models import InferencePlan, LatencyContract
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class ProviderCapabilities:
10
+ """Execution features a provider can honor."""
11
+
12
+ streaming: bool = True
13
+ model_mapping: bool = True
14
+ precision: bool = False
15
+ context_policy: bool = False
16
+ speculation: bool = False
17
+ early_exit: bool = False
18
+ usage_metadata: bool = False
19
+ queue_metrics: bool = False
20
+ cache_metrics: bool = False
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class EnforcedPlan:
25
+ """Requested plan, provider-supported plan, and unapplied choices."""
26
+
27
+ requested: InferencePlan
28
+ enforced: InferencePlan
29
+ unsupported: tuple[str, ...] = ()
30
+
31
+
32
+ def enforce_plan(
33
+ contract: LatencyContract,
34
+ plan: InferencePlan,
35
+ capabilities: ProviderCapabilities,
36
+ ) -> EnforcedPlan:
37
+ """Remove unsupported optimizations while preserving critical safeguards."""
38
+ unsupported: list[str] = []
39
+ precision = plan.precision
40
+ context_policy = plan.context_policy
41
+ speculative_tokens = plan.speculative_tokens
42
+ early_exit = plan.early_exit
43
+ rationale = list(plan.rationale)
44
+ if precision != "fp16" and not capabilities.precision:
45
+ unsupported.append("precision")
46
+ precision = "fp16"
47
+ if context_policy != "full" and not capabilities.context_policy:
48
+ unsupported.append("context_policy")
49
+ context_policy = "full"
50
+ if speculative_tokens and not capabilities.speculation:
51
+ unsupported.append("speculation")
52
+ speculative_tokens = 0
53
+ if early_exit and not capabilities.early_exit:
54
+ unsupported.append("early_exit")
55
+ early_exit = False
56
+ if unsupported:
57
+ rationale.append("provider cannot enforce: " + ", ".join(unsupported))
58
+ enforced = InferencePlan(
59
+ plan.model_tier, precision, context_policy, speculative_tokens, early_exit, tuple(rationale)
60
+ )
61
+ if contract.risk_class == "critical" and enforced != plan:
62
+ enforced = InferencePlan(
63
+ plan.model_tier, "fp16", "full", 0, False,
64
+ tuple(rationale + ["critical safeguards enforced at provider boundary"]),
65
+ )
66
+ return EnforcedPlan(plan, enforced, tuple(unsupported))
latencyops/cli.py ADDED
@@ -0,0 +1,54 @@
1
+ """Command-line entry point for the LatencyOps reference application."""
2
+
3
+ import argparse
4
+ import json
5
+ from dataclasses import asdict
6
+
7
+ from .config import build_service, load_config
8
+ from .models import LatencyContract, RequestProfile, SystemSignals
9
+ from .policy import ElasticInferencePlanner
10
+
11
+
12
+ def main(argv: list[str] | None = None) -> int:
13
+ parser = argparse.ArgumentParser(prog="latencyops")
14
+ subparsers = parser.add_subparsers(dest="command", required=True)
15
+ gateway = subparsers.add_parser("gateway", help="start the configured HTTP gateway")
16
+ gateway.add_argument("--config", required=True)
17
+ plan = subparsers.add_parser("plan", help="calculate a plan from a JSON request")
18
+ plan.add_argument("--request", required=True)
19
+ health = subparsers.add_parser("health", help="check configured provider health")
20
+ health.add_argument("--config", required=True)
21
+ args = parser.parse_args(argv)
22
+ if args.command == "plan":
23
+ with open(args.request, encoding="utf-8") as handle:
24
+ request = json.load(handle)
25
+ contract = LatencyContract(
26
+ deadline_ms=float(request.get("deadline_ms", 1000)),
27
+ quality_floor=float(request.get("quality_floor", 0.9)),
28
+ risk_class=request.get("risk_class", "medium"),
29
+ )
30
+ profile = RequestProfile(
31
+ prompt_tokens=int(request.get("prompt_tokens", 0)),
32
+ expected_output_tokens=int(request.get("expected_output_tokens", 16)),
33
+ difficulty=float(request.get("difficulty", 0.5)),
34
+ cache_pressure=float(request.get("cache_pressure", 0)),
35
+ queue_pressure=float(request.get("queue_pressure", 0)),
36
+ )
37
+ print(json.dumps(asdict(ElasticInferencePlanner().plan(contract, profile, SystemSignals(
38
+ queue_pressure=profile.queue_pressure, cache_pressure=profile.cache_pressure,
39
+ ))), indent=2))
40
+ return 0
41
+ config = load_config(args.config)
42
+ if args.command == "health":
43
+ results = {name: provider.health().__dict__ if hasattr(provider, "health") else {"healthy": True, "detail": "not configured"} for name, provider in config.providers.items()}
44
+ print(json.dumps(results, indent=2))
45
+ return 0 if all(result["healthy"] for result in results.values()) else 1
46
+ service = build_service(config)
47
+ from .gateway import create_server
48
+ print(f"LatencyOps gateway listening on http://{config.host}:{config.port}")
49
+ create_server(config.host, config.port, service).serve_forever()
50
+ return 0
51
+
52
+
53
+ if __name__ == "__main__":
54
+ raise SystemExit(main())
@@ -0,0 +1,52 @@
1
+ """Benchmark comparison and constraint-satisfaction helpers."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Iterable
5
+
6
+ from .benchmark import BenchmarkRunner
7
+ from .metrics import summarize
8
+ from .models import InferencePlan, LatencyContract, LatencySample, RequestProfile
9
+ from .router import StreamingProvider
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class WorkloadCase:
14
+ """Synthetic workload metadata; prompt content is never included in reports."""
15
+
16
+ name: str
17
+ prompt: str
18
+ contract: LatencyContract
19
+ profile: RequestProfile
20
+
21
+
22
+ def run_workload_matrix(
23
+ provider: StreamingProvider,
24
+ cases: Iterable[WorkloadCase],
25
+ plan: InferencePlan,
26
+ runner: BenchmarkRunner | None = None,
27
+ ) -> list[dict[str, object]]:
28
+ """Run cases and report measurements plus contract satisfaction."""
29
+ runner = runner or BenchmarkRunner()
30
+ rows = []
31
+ for case in cases:
32
+ result = runner.run_streaming(provider, case.prompt, plan)
33
+ sample = result.sample
34
+ rows.append({
35
+ "case": case.name,
36
+ "success": sample.success,
37
+ "ttft_ms": sample.ttft_ms,
38
+ "tpot_ms": sample.tpot_ms,
39
+ "end_to_end_ms": sample.end_to_end_ms,
40
+ "deadline_satisfied": sample.success and sample.end_to_end_ms <= case.contract.deadline_ms,
41
+ "quality_floor": case.contract.quality_floor,
42
+ })
43
+ return rows
44
+
45
+
46
+ def compare(samples_by_provider: dict[str, list[LatencySample]]) -> list[dict[str, object]]:
47
+ """Return stable, ranked summaries without retaining prompts or responses."""
48
+ rows = []
49
+ for provider, samples in samples_by_provider.items():
50
+ summary = summarize(samples)
51
+ rows.append({"provider": provider, **summary})
52
+ return sorted(rows, key=lambda row: (row.get("e2e_p95_ms", float("inf")), row["provider"]))
latencyops/config.py ADDED
@@ -0,0 +1,71 @@
1
+ """TOML configuration for the installable LatencyOps application."""
2
+
3
+ import os
4
+ import tomllib
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from .adapters import OpenAIChatCompatibleProvider
9
+ from .gateway import GatewayService
10
+ from .models import ProviderCandidate
11
+ from .policy import ElasticInferencePlanner
12
+ from .router import ModelRouter
13
+ from .telemetry import PrometheusExporter, TelemetryRecorder
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class AppConfig:
18
+ """Validated runtime configuration loaded from TOML and environment variables."""
19
+
20
+ host: str
21
+ port: int
22
+ providers: dict[str, OpenAIChatCompatibleProvider]
23
+ candidates: list[ProviderCandidate]
24
+
25
+
26
+ def load_config(path: str | Path) -> AppConfig:
27
+ with Path(path).open("rb") as handle:
28
+ raw = tomllib.load(handle)
29
+ server = raw.get("server", {})
30
+ provider_table = raw.get("providers", {})
31
+ providers: dict[str, OpenAIChatCompatibleProvider] = {}
32
+ candidates: list[ProviderCandidate] = []
33
+ for name, settings in provider_table.items():
34
+ endpoint = settings.get("endpoint")
35
+ key_env = settings.get("api_key_env")
36
+ if not endpoint or not key_env:
37
+ raise ValueError(f"provider {name!r} requires endpoint and api_key_env")
38
+ key = os.environ.get(key_env)
39
+ if not key:
40
+ raise ValueError(f"environment variable {key_env!r} is not set")
41
+ models = settings.get("models", {})
42
+ provider = OpenAIChatCompatibleProvider(
43
+ name,
44
+ endpoint,
45
+ api_key=key,
46
+ timeout=float(settings.get("timeout_seconds", 30)),
47
+ model_names=models,
48
+ chat_template_kwargs=settings.get("chat_template_kwargs"),
49
+ health_endpoint=settings.get("health_endpoint"),
50
+ )
51
+ providers[name] = provider
52
+ for tier, model in models.items():
53
+ candidates.append(ProviderCandidate(name, tier, True, cost_per_1k_tokens=settings.get("cost_per_1k_tokens")))
54
+ missing = {"small", "standard", "large"} - {candidate.model_tier for candidate in candidates}
55
+ if missing:
56
+ raise ValueError(f"configuration is missing model tiers: {sorted(missing)}")
57
+ return AppConfig(str(server.get("host", "127.0.0.1")), int(server.get("port", 8080)), providers, candidates)
58
+
59
+
60
+ def build_service(config: AppConfig) -> GatewayService:
61
+ exporter = PrometheusExporter()
62
+ telemetry = TelemetryRecorder([exporter])
63
+ lookup = {candidate.name: config.providers[candidate.name] for candidate in config.candidates}
64
+ by_tier = {
65
+ candidate.model_tier: config.providers[candidate.name]
66
+ for candidate in config.candidates
67
+ }
68
+ return GatewayService(
69
+ ModelRouter(by_tier), ElasticInferencePlanner(), telemetry,
70
+ provider_candidates=config.candidates, provider_lookup=lookup,
71
+ )