byokit 0.0.1__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.
byokit/__init__.py ADDED
@@ -0,0 +1,1299 @@
1
+ """Offline discovery and stateless, explicitly invoked inference."""
2
+
3
+ from .enums import *
4
+ import copy
5
+ import math
6
+ from datetime import date
7
+
8
+ FEATURE_STATES = {"supported", "unsupported", "conditional", "unknown"}
9
+
10
+
11
+ def finite(n):
12
+ return (
13
+ isinstance(n, (int, float))
14
+ and not isinstance(n, bool)
15
+ and math.isfinite(n)
16
+ and n >= 0
17
+ )
18
+
19
+
20
+ def positive(n):
21
+ return finite(n) and n > 0
22
+
23
+
24
+ def diagnostic(code, message, severity="warning", offering_id=None):
25
+ return dict(
26
+ code=code,
27
+ message=message,
28
+ severity=severity,
29
+ **({"offeringId": offering_id} if offering_id else {}),
30
+ )
31
+
32
+
33
+ def validate_catalog(data):
34
+ errors = []
35
+ if not isinstance(data, dict) or data.get("schemaVersion") != 1:
36
+ return ["Unsupported schemaVersion"]
37
+ for key in ["providers", "models", "families", "offerings", "presets"]:
38
+ if not isinstance(data.get(key), list):
39
+ errors.append(f"{key} must be an array")
40
+ if errors:
41
+ return errors
42
+ for key in ["providers", "models", "families", "offerings", "presets"]:
43
+ seen = set()
44
+ for item in data[key]:
45
+ item_id = item.get("id")
46
+ if not isinstance(item_id, str) or not item_id or item_id in seen:
47
+ errors.append(f"{key}: invalid or duplicate id")
48
+ seen.add(item_id)
49
+ providers = {p["id"] for p in data["providers"]}
50
+ models = {m["id"] for m in data["models"]}
51
+ for o in data["offerings"]:
52
+ errors.extend(_validate_signals(o))
53
+ if o.get("releaseDate") is not None:
54
+ try:
55
+ if date.fromisoformat(o["releaseDate"]).isoformat() != o["releaseDate"]:
56
+ raise ValueError()
57
+ except (ValueError, TypeError):
58
+ errors.append(f"{o['id']}: invalid releaseDate")
59
+ if "connection" in o:
60
+ try:
61
+ validate_connection(o["connection"])
62
+ except ValueError:
63
+ errors.append(o["id"] + ": invalid connection")
64
+ if o.get("providerId") not in providers or o.get("modelId") not in models:
65
+ errors.append(f"{o['id']}: unresolved reference")
66
+ if o.get("deployment") not in ["hosted", "local", "self-hosted"]:
67
+ errors.append(f"{o['id']}: invalid deployment")
68
+ for key, value in o.get("limits", {}).items():
69
+ if key != "contextAccounting" and value is not None and not positive(value):
70
+ errors.append(f"{o['id']}: invalid {key}")
71
+ for feature in o.get("features", {}).values():
72
+ if feature.get("status") not in FEATURE_STATES:
73
+ errors.append(f"{o['id']}: invalid feature status")
74
+ for value in o.get("price", {}).values():
75
+ if isinstance(value, (int, float)) and not finite(value):
76
+ errors.append(f"{o['id']}: invalid price")
77
+ for p in data["presets"]:
78
+ if not isinstance(p.get("providerIds"), list) or any(
79
+ i not in providers for i in p["providerIds"]
80
+ ):
81
+ errors.append(f"{p['id']}: unresolved provider")
82
+ errors.extend(validate_verifications(data.get("verifications", []), data))
83
+ return errors
84
+
85
+
86
+ def validate_config(config):
87
+ errors = []
88
+ if "sort" in config and config["sort"] not in {v.value for v in ModelSort}:
89
+ errors.append("Unknown model sort")
90
+ if "list" in config and config["list"] not in {v.value for v in ModelList}:
91
+ errors.append("Unknown model list")
92
+ if "local" in config and config["local"] is not False:
93
+ if not isinstance(config["local"], list):
94
+ errors.append("local must be false or an array")
95
+ else:
96
+ for entry in config["local"]:
97
+ try:
98
+ _validate_local(entry)
99
+ except ValueError as error:
100
+ errors.append(str(error))
101
+ allowed = {
102
+ "root": [
103
+ "sort",
104
+ "list",
105
+ "local",
106
+ "limit",
107
+ "asOf",
108
+ "unknownPolicy",
109
+ "schemaVersion",
110
+ "catalog",
111
+ "preset",
112
+ "providerIds",
113
+ "filters",
114
+ "prefer",
115
+ "ui",
116
+ "budget",
117
+ "tasks",
118
+ ],
119
+ "filters": [
120
+ "deployment",
121
+ "origin",
122
+ "openWeights",
123
+ "commercialUse",
124
+ "input",
125
+ "output",
126
+ "requiredFeatures",
127
+ "minContextTokens",
128
+ "maxInputPricePerM",
129
+ "maxOutputPricePerM",
130
+ "maxParameters",
131
+ "maxTimeToFirstTokenMs",
132
+ "minTokensPerSecond",
133
+ "fileFormats",
134
+ "includeTags",
135
+ "excludeTags",
136
+ "lifecycle",
137
+ "popularity",
138
+ "includeDeprecated",
139
+ "verified",
140
+ "verifiedBy",
141
+ "verifiedWithinDays",
142
+ ],
143
+ "prefer": ["features", "lowerCost"],
144
+ "ui": ["maxProviders", "maxModelsPerProvider", "allowCustomModel"],
145
+ "budget": ["maxInputTokens", "maxOutputTokens", "overheadTokens"],
146
+ }
147
+ for group, keys in allowed.items():
148
+ for key in config if group == "root" else config.get(group, {}) or {}:
149
+ if key not in keys:
150
+ errors.append(f"Unknown {group}.{key}")
151
+ for key in [
152
+ "deployment",
153
+ "origin",
154
+ "input",
155
+ "output",
156
+ "requiredFeatures",
157
+ "fileFormats",
158
+ "includeTags",
159
+ "excludeTags",
160
+ "lifecycle",
161
+ "popularity",
162
+ ]:
163
+ if key in config.get("filters", {}) and (
164
+ not isinstance(config["filters"][key], list)
165
+ or any(not isinstance(v, str) for v in config["filters"][key])
166
+ ):
167
+ errors.append(f"filters.{key} must be a string array")
168
+ for key in [
169
+ "minContextTokens",
170
+ "maxInputPricePerM",
171
+ "maxOutputPricePerM",
172
+ "maxParameters",
173
+ "maxTimeToFirstTokenMs",
174
+ "minTokensPerSecond",
175
+ ]:
176
+ if key in config.get("filters", {}) and not finite(config["filters"][key]):
177
+ errors.append(f"filters.{key} must be a nonnegative finite number")
178
+ for key in ["maxProviders", "maxModelsPerProvider"]:
179
+ value = config.get("ui", {}).get(key)
180
+ if value is not None and (
181
+ not isinstance(value, int)
182
+ or isinstance(value, bool)
183
+ or not 1 <= value <= 100
184
+ ):
185
+ errors.append(f"ui.{key} must be an integer from 1 to 100")
186
+ import re
187
+
188
+ if "limit" in config and (
189
+ not isinstance(config["limit"], int)
190
+ or isinstance(config["limit"], bool)
191
+ or not 1 <= config["limit"] <= 1000
192
+ ):
193
+ errors.append("limit must be an integer from 1 to 1000")
194
+ if "asOf" in config:
195
+ try:
196
+ if not isinstance(config["asOf"], str) or not re.fullmatch(
197
+ r"\d{4}-\d{2}-\d{2}T.*Z", config["asOf"]
198
+ ):
199
+ raise ValueError("UTC timestamp required")
200
+ _timestamp(config["asOf"])
201
+ except (ValueError, TypeError):
202
+ errors.append("asOf must be an ISO timestamp")
203
+ f = config.get("filters", {})
204
+ if "verified" in f and not isinstance(f["verified"], bool):
205
+ errors.append("filters.verified must be boolean")
206
+ if "verifiedBy" in f and (
207
+ not isinstance(f["verifiedBy"], str)
208
+ or not re.fullmatch(r"github:[a-z0-9-]+", f["verifiedBy"], re.I)
209
+ ):
210
+ errors.append("verifiedBy must identify a GitHub contributor")
211
+ if "verifiedWithinDays" in f and (
212
+ not finite(f["verifiedWithinDays"]) or f["verifiedWithinDays"] < 1
213
+ ):
214
+ errors.append("verifiedWithinDays must be positive")
215
+ vocab = {
216
+ "input": Modality,
217
+ "output": Modality,
218
+ "requiredFeatures": FeatureName,
219
+ "deployment": Deployment,
220
+ "lifecycle": Lifecycle,
221
+ "popularity": Popularity,
222
+ }
223
+ for key, enum in vocab.items():
224
+ values = f.get(key)
225
+ if isinstance(values, list) and any(
226
+ v not in {m.value for m in enum} for v in values
227
+ ):
228
+ errors.append(f"Unknown filters.{key} value")
229
+ preferred = config.get("prefer", {}).get("features")
230
+ if preferred is not None and (
231
+ not isinstance(preferred, list)
232
+ or any(v not in {m.value for m in FeatureName} for v in preferred)
233
+ ):
234
+ errors.append("Unknown preferred feature")
235
+ if "commercialUse" in f and f["commercialUse"] not in {
236
+ m.value for m in CommercialUse
237
+ }:
238
+ errors.append("Unknown commercialUse value")
239
+ if config.get("unknownPolicy", "exclude") not in ["include", "exclude"]:
240
+ errors.append("Invalid unknownPolicy")
241
+ for key in ["maxInputTokens", "maxOutputTokens", "overheadTokens"]:
242
+ if key in config.get("budget", {}) and not finite(config["budget"][key]):
243
+ errors.append(f"budget.{key} must be a nonnegative finite number")
244
+ return errors
245
+
246
+
247
+ def matches(o, m, config, provider_tags=None):
248
+ f = config.get("filters", {})
249
+ unknown = config.get("unknownPolicy") == "include"
250
+ if "deployment" in f and o["deployment"] not in f["deployment"]:
251
+ return False
252
+ if "origin" in f and m.get("origin") not in f["origin"]:
253
+ return False
254
+ if "openWeights" in f:
255
+ value = m.get("openWeights")
256
+ if (value is None and not unknown) or (
257
+ value is not None and value != f["openWeights"]
258
+ ):
259
+ return False
260
+ if "commercialUse" in f:
261
+ value = m.get("commercialUse", "unknown")
262
+ if (value == "unknown" and not unknown) or (
263
+ value != "unknown" and value != f["commercialUse"]
264
+ ):
265
+ return False
266
+ for direction in ["input", "output"]:
267
+ required = f.get(direction, [])
268
+ values = o.get("modalities", {}).get(direction)
269
+ if required and (
270
+ (values is None and not unknown)
271
+ or (values is not None and not all(v in values for v in required))
272
+ ):
273
+ return False
274
+ if f.get("fileFormats") and (
275
+ (o.get("fileFormats") is None and not unknown)
276
+ or (
277
+ o.get("fileFormats") is not None
278
+ and not all(v in o["fileFormats"] for v in f["fileFormats"])
279
+ )
280
+ ):
281
+ return False
282
+ for feature in f.get("requiredFeatures", []):
283
+ state = o.get("features", {}).get(feature, {}).get("status", "unknown")
284
+ if state != "supported" and not (state == "unknown" and unknown):
285
+ return False
286
+ constraints = [
287
+ ("minContextTokens", o.get("limits", {}).get("context"), "min"),
288
+ ("maxInputPricePerM", o.get("price", {}).get("input"), "max"),
289
+ ("maxOutputPricePerM", o.get("price", {}).get("output"), "max"),
290
+ ("maxParameters", m.get("size", {}).get("totalParameters"), "max"),
291
+ ("maxTimeToFirstTokenMs", o.get("speed", {}).get("timeToFirstTokenMs"), "max"),
292
+ ("minTokensPerSecond", o.get("speed", {}).get("tokensPerSecond"), "min"),
293
+ ]
294
+ for key, value, op in constraints:
295
+ if key in f and (
296
+ (not finite(value) and not unknown)
297
+ or (finite(value) and (value < f[key] if op == "min" else value > f[key]))
298
+ ):
299
+ return False
300
+ if o["lifecycle"]["status"] not in f.get("lifecycle", [Lifecycle.LIVE]):
301
+ return False
302
+ if "popularity" in f and o["popularity"]["status"] not in f["popularity"]:
303
+ return False
304
+ tags = set(
305
+ [
306
+ t
307
+ for t in m.get("tags", []) + (provider_tags or [])
308
+ if not t.startswith(("lifecycle:", "popularity:"))
309
+ ]
310
+ + o.get("tags", [])
311
+ )
312
+ if any(t not in tags for t in f.get("includeTags", [])) or any(
313
+ t in tags for t in f.get("excludeTags", [])
314
+ ):
315
+ return False
316
+ return f.get("includeDeprecated", False) or o.get("status") != "deprecated"
317
+
318
+
319
+ class Catalog:
320
+ def __init__(self, data):
321
+ errors = validate_catalog(data)
322
+ if errors:
323
+ raise ValueError("; ".join(errors))
324
+ self.data = copy.deepcopy(data)
325
+ self.providers = {p["id"]: p for p in self.data["providers"]}
326
+ self.models = {m["id"]: m for m in self.data["models"]}
327
+ self.offerings = {o["id"]: o for o in self.data["offerings"]}
328
+
329
+ def get(self, offering_id):
330
+ return self.offerings.get(offering_id)
331
+
332
+ def get_models(
333
+ self,
334
+ config: "PickerConfig | ModelList | None" = None,
335
+ options: "PickerConfig | None" = None,
336
+ ) -> list[dict]:
337
+ config = _normalize_query(config, options)
338
+ groups = self._select(config, True)
339
+ result = []
340
+ for g in groups:
341
+ for o in g["offerings"]:
342
+ model = self.models.get(
343
+ o["modelId"],
344
+ {
345
+ "id": o["modelId"],
346
+ "name": o["name"],
347
+ "openWeights": None,
348
+ "commercialUse": "unknown",
349
+ "license": None,
350
+ "tags": [],
351
+ },
352
+ )
353
+ family = next(
354
+ (
355
+ f
356
+ for f in self.data["families"]
357
+ if f["id"] == model.get("familyId")
358
+ ),
359
+ None,
360
+ )
361
+ assets = self.data.get("assets", [])
362
+ result.append(
363
+ dict(
364
+ o,
365
+ connection=connection_for(o),
366
+ provider=g["provider"],
367
+ model=model,
368
+ family=family,
369
+ logos={
370
+ "provider": next(
371
+ (
372
+ a
373
+ for a in assets
374
+ if a["id"] == g["provider"].get("logoId")
375
+ ),
376
+ None,
377
+ ),
378
+ "family": next(
379
+ (
380
+ a
381
+ for a in assets
382
+ if family and a["id"] == family.get("logoId")
383
+ ),
384
+ None,
385
+ ),
386
+ },
387
+ verification=verification_summary(
388
+ self.data,
389
+ o["id"],
390
+ as_of=config.get("asOf"),
391
+ max_age_days=config.get("filters", {}).get(
392
+ "verifiedWithinDays", 90
393
+ ),
394
+ contributor=config.get("filters", {}).get("verifiedBy"),
395
+ ),
396
+ )
397
+ )
398
+ hosted = [o for o in result if o["deployment"] != "local"]
399
+ sort = config.get("sort", ModelSort.ALPHABETICAL_ASCEND)
400
+ sort = {
401
+ "alphabetical": ModelSort.ALPHABETICAL_ASCEND,
402
+ "price": ModelSort.PRICE_ASCEND,
403
+ "speed": ModelSort.SPEED_DESCEND,
404
+ }.get(sort, sort)
405
+ if sort != ModelSort.PROVIDER_ORDER:
406
+ from functools import cmp_to_key
407
+
408
+ def ordering(a, b):
409
+ def compare(x, y):
410
+ return (x > y) - (x < y)
411
+
412
+ result = 0
413
+ if sort in [
414
+ ModelSort.PRICE_ASCEND,
415
+ ModelSort.PRICE_DESCEND,
416
+ ModelSort.SPEED_ASCEND,
417
+ ModelSort.SPEED_DESCEND,
418
+ ]:
419
+ price = sort in [ModelSort.PRICE_ASCEND, ModelSort.PRICE_DESCEND]
420
+
421
+ def value(o):
422
+ return (
423
+ o.get("price", {}).get("input")
424
+ if o.get("price", {}).get("currency") == "USD"
425
+ else None
426
+ )
427
+
428
+ x = (
429
+ value(a)
430
+ if price
431
+ else (a.get("speed") or {}).get("tokensPerSecond")
432
+ )
433
+ y = (
434
+ value(b)
435
+ if price
436
+ else (b.get("speed") or {}).get("tokensPerSecond")
437
+ )
438
+ if x is None or y is None:
439
+ result = -1 if x is not None else 1 if y is not None else 0
440
+ else:
441
+ result = compare(x, y) * (
442
+ 1
443
+ if sort in [ModelSort.PRICE_ASCEND, ModelSort.SPEED_ASCEND]
444
+ else -1
445
+ )
446
+ return (
447
+ result
448
+ or compare(a["name"].lower(), b["name"].lower())
449
+ * (-1 if sort == ModelSort.ALPHABETICAL_DESCEND else 1)
450
+ or compare(a["providerId"], b["providerId"])
451
+ or compare(a["id"], b["id"])
452
+ )
453
+
454
+ hosted.sort(key=cmp_to_key(ordering))
455
+ return copy.deepcopy(
456
+ hosted[: config.get("limit", len(result))]
457
+ + [o for o in result if o["deployment"] == "local"]
458
+ )
459
+
460
+ def select(self, config=None, options=None):
461
+ return self._select(_normalize_query(config, options))
462
+
463
+ def _select(self, config=None, uncapped=False):
464
+ config = config or {}
465
+ errors = validate_config(config)
466
+ if errors:
467
+ raise ValueError("; ".join(errors))
468
+ preset = next(
469
+ (
470
+ p
471
+ for p in self.data["presets"]
472
+ if p["id"]
473
+ == config.get(
474
+ "preset",
475
+ "default" if config.get("list") == ModelList.DEFAULT else "global",
476
+ )
477
+ ),
478
+ None,
479
+ )
480
+ if not preset and config.get("list") == ModelList.DEFAULT:
481
+ preset = next(
482
+ (p for p in self.data["presets"] if p["id"] == "global"), None
483
+ )
484
+ if not preset:
485
+ raise ValueError("Unknown preset")
486
+ ids = config.get(
487
+ "providerIds",
488
+ list(self.providers)
489
+ if config.get("list") == ModelList.COMPLETE
490
+ else preset["providerIds"],
491
+ )
492
+ if any(i not in self.providers for i in ids):
493
+ raise ValueError("Unknown provider in providerIds")
494
+ groups = []
495
+
496
+ def order(o):
497
+ count = sum(
498
+ o.get("features", {}).get(f, {}).get("status") == "supported"
499
+ for f in config.get("prefer", {}).get("features", [])
500
+ )
501
+ price = o.get("price", {}).get("input")
502
+ return (
503
+ -count,
504
+ (price if price is not None else math.inf)
505
+ if config.get("prefer", {}).get("lowerCost")
506
+ else 0,
507
+ o.get("rank", 100),
508
+ o["id"],
509
+ )
510
+
511
+ for provider_id in ids:
512
+ if self.providers[provider_id]["deployment"] == "local":
513
+ continue
514
+ choices = sorted(
515
+ (
516
+ o
517
+ for o in (
518
+ _with_signals(item, config.get("asOf"))
519
+ for item in self.data["offerings"]
520
+ if item["providerId"] == provider_id
521
+ )
522
+ if matches(
523
+ o,
524
+ self.models[o["modelId"]],
525
+ config,
526
+ self.providers[provider_id].get("tags", []),
527
+ )
528
+ and matches_verification(self.data, o["id"], config)
529
+ ),
530
+ key=order,
531
+ )
532
+ cap = (
533
+ (
534
+ len(choices)
535
+ if config.get("list") == ModelList.COMPLETE
536
+ or "providerIds" in config
537
+ else config.get("ui", {}).get("maxModelsPerProvider", 1)
538
+ )
539
+ if uncapped
540
+ else config.get("ui", {}).get("maxModelsPerProvider", 4)
541
+ )
542
+ if choices:
543
+ groups.append(
544
+ {
545
+ "provider": self.providers[provider_id],
546
+ "offerings": choices[:cap],
547
+ }
548
+ )
549
+ locals_ = _local_groups(config, self)
550
+ return (
551
+ groups
552
+ if uncapped
553
+ else groups[
554
+ : max(0, config.get("ui", {}).get("maxProviders", 8) - len(locals_))
555
+ ]
556
+ ) + locals_
557
+
558
+
559
+ def check_fit(offering, budget, options=None):
560
+ options = options or {}
561
+ if not offering:
562
+ return {
563
+ "status": "unknown",
564
+ "certainty": "unknown",
565
+ "diagnostics": [
566
+ diagnostic(
567
+ "MODEL_UNVERIFIED",
568
+ "Custom model limits and capabilities are unverified.",
569
+ )
570
+ ],
571
+ }
572
+ for key in ["inputTokens", "outputTokens", "overheadTokens"]:
573
+ if key in budget and budget[key] is not None and not finite(budget[key]):
574
+ raise ValueError(f"Invalid {key}")
575
+ input_tokens = (budget.get("inputTokens") or 0) + (
576
+ budget.get("overheadTokens") or 0
577
+ )
578
+ output = budget.get("outputTokens") or 0
579
+ limits = offering.get("limits", {})
580
+ diagnostics = []
581
+ oid = offering["id"]
582
+ for f in options.get("requiredFeatures", []):
583
+ state = offering.get("features", {}).get(f, {}).get("status", "unknown")
584
+ if state != "supported":
585
+ diagnostics.append(
586
+ diagnostic(
587
+ "CAPABILITY_UNSUPPORTED"
588
+ if state == "unsupported"
589
+ else "CAPABILITY_UNVERIFIED",
590
+ f"{f}: {state}",
591
+ "error" if state == "unsupported" else "warning",
592
+ oid,
593
+ )
594
+ )
595
+ for direction in ["input", "output"]:
596
+ for modality in options.get("modalities", {}).get(direction, []):
597
+ values = offering.get("modalities", {}).get(direction)
598
+ if values is None or modality not in values:
599
+ diagnostics.append(
600
+ diagnostic(
601
+ "MODALITY_UNVERIFIED"
602
+ if values is None
603
+ else "MODALITY_UNSUPPORTED",
604
+ f"{direction} modality {modality} is {'unverified' if values is None else 'unsupported'}.",
605
+ "warning" if values is None else "error",
606
+ oid,
607
+ )
608
+ )
609
+ if positive(limits.get("input")) and input_tokens > limits["input"]:
610
+ diagnostics.append(
611
+ diagnostic(
612
+ "INPUT_BUDGET_EXCEEDED",
613
+ f"Input {input_tokens} exceeds {limits['input']}.",
614
+ "error",
615
+ oid,
616
+ )
617
+ )
618
+ if positive(limits.get("output")) and output > limits["output"]:
619
+ diagnostics.append(
620
+ diagnostic(
621
+ "OUTPUT_BUDGET_EXCEEDED",
622
+ f"Output {output} exceeds {limits['output']}.",
623
+ "error",
624
+ oid,
625
+ )
626
+ )
627
+ used = (
628
+ input_tokens + output
629
+ if limits.get("contextAccounting") == "combined"
630
+ else input_tokens
631
+ )
632
+ ratio = None
633
+ if positive(limits.get("context")) and limits.get("contextAccounting") in [
634
+ "combined",
635
+ "input-only",
636
+ ]:
637
+ ratio = used / limits["context"]
638
+ if ratio > 1:
639
+ diagnostics.append(
640
+ diagnostic(
641
+ "CONTEXT_BUDGET_EXCEEDED",
642
+ f"Required {used} exceeds context {limits['context']}.",
643
+ "error",
644
+ oid,
645
+ )
646
+ )
647
+ elif ratio >= 0.8:
648
+ diagnostics.append(
649
+ diagnostic(
650
+ "CONTEXT_NEAR_LIMIT",
651
+ f"Context budget uses {math.floor(ratio * 100 + 0.5)}%.",
652
+ "warning",
653
+ oid,
654
+ )
655
+ )
656
+ else:
657
+ if positive(limits.get("context")) and input_tokens > limits["context"]:
658
+ diagnostics.append(
659
+ diagnostic(
660
+ "CONTEXT_BUDGET_EXCEEDED",
661
+ f"Input {input_tokens} exceeds context {limits['context']}.",
662
+ "error",
663
+ oid,
664
+ )
665
+ )
666
+ diagnostics.append(
667
+ diagnostic(
668
+ "LOCAL_CONTEXT_UNKNOWN"
669
+ if offering.get("deployment") == "local"
670
+ else "CONTEXT_UNVERIFIED",
671
+ "Effective context or its accounting rules are unknown.",
672
+ offering_id=oid,
673
+ )
674
+ )
675
+ if output > 0 and not positive(limits.get("output")):
676
+ diagnostics.append(
677
+ diagnostic(
678
+ "OUTPUT_LIMIT_UNKNOWN", "Output limit is unknown.", offering_id=oid
679
+ )
680
+ )
681
+ if options.get("estimate"):
682
+ diagnostics.append(
683
+ diagnostic(
684
+ "TOKEN_ESTIMATE",
685
+ "Token count is estimated; this is not an exact fit guarantee.",
686
+ offering_id=oid,
687
+ )
688
+ )
689
+ if offering.get("status") == "deprecated":
690
+ diagnostics.append(
691
+ diagnostic(
692
+ "MODEL_DEPRECATED", "Selected model is deprecated.", offering_id=oid
693
+ )
694
+ )
695
+ unknown_codes = {
696
+ "CONTEXT_UNVERIFIED",
697
+ "LOCAL_CONTEXT_UNKNOWN",
698
+ "OUTPUT_LIMIT_UNKNOWN",
699
+ "CAPABILITY_UNVERIFIED",
700
+ "MODALITY_UNVERIFIED",
701
+ "TOKEN_ESTIMATE",
702
+ }
703
+ status = (
704
+ "exceeds"
705
+ if any(d["severity"] == "error" for d in diagnostics)
706
+ else "unknown"
707
+ if any(d["code"] in unknown_codes for d in diagnostics)
708
+ else "fits"
709
+ )
710
+ return dict(
711
+ status=status,
712
+ certainty="estimated"
713
+ if options.get("estimate")
714
+ else options.get("certainty", "declared"),
715
+ inputTokens=input_tokens,
716
+ outputTokens=output,
717
+ diagnostics=diagnostics,
718
+ **({"ratio": ratio} if ratio is not None else {}),
719
+ )
720
+
721
+
722
+ def check_task(catalog, task):
723
+ if not task.get("offeringIds"):
724
+ return [
725
+ diagnostic("NO_MODELS", "Task requires at least one offering.", "error")
726
+ ]
727
+ result = []
728
+ b = task.get("budget", {})
729
+ r = task.get("requires", {})
730
+ if b.get("maxInputTokens") is None or b.get("maxOutputTokens") is None:
731
+ result.append(
732
+ diagnostic(
733
+ "BUDGET_UNDECLARED",
734
+ "Declare input and output token budgets for build-time checks.",
735
+ )
736
+ )
737
+ for oid in task["offeringIds"]:
738
+ fit = check_fit(
739
+ catalog.get(oid),
740
+ {
741
+ "inputTokens": b.get("maxInputTokens"),
742
+ "outputTokens": b.get("maxOutputTokens"),
743
+ "overheadTokens": b.get("overheadTokens"),
744
+ },
745
+ {
746
+ "requiredFeatures": r.get("features", []),
747
+ "modalities": r.get("modalities", {}),
748
+ },
749
+ )
750
+ result.extend(dict(d, offeringId=oid) for d in fit["diagnostics"])
751
+ return result
752
+
753
+
754
+ def estimate_price(offering, budget):
755
+ i = budget["inputTokens"]
756
+ o = budget["outputTokens"]
757
+ if not finite(i) or not finite(o):
758
+ raise ValueError("Invalid token counts")
759
+ p = offering.get("price", {})
760
+ if (
761
+ p.get("currency") != "USD"
762
+ or not finite(p.get("input"))
763
+ or not finite(p.get("output"))
764
+ or p.get("tiers")
765
+ ):
766
+ return {"status": "unknown"}
767
+ return {
768
+ "status": "estimated",
769
+ "currency": "USD",
770
+ "amount": (i * p["input"] + o * p["output"]) / 1e6,
771
+ "excludes": [
772
+ "cache pricing",
773
+ "additional reasoning charges",
774
+ "tax",
775
+ "tier changes",
776
+ ],
777
+ }
778
+
779
+
780
+ def load_catalog():
781
+ """Load the packaged offline snapshot. No network or Node dependency."""
782
+ import json
783
+ from importlib.resources import files
784
+
785
+ return Catalog(
786
+ json.loads(files("byokit").joinpath("data/catalog.json").read_text())
787
+ )
788
+
789
+
790
+ def derive_tags(offering, model=None):
791
+ model = model or {}
792
+ prefixes = (
793
+ "cap:",
794
+ "input:",
795
+ "output:",
796
+ "price:",
797
+ "context:",
798
+ "weights:",
799
+ "origin:",
800
+ "deployment:",
801
+ "lifecycle:",
802
+ "popularity:",
803
+ )
804
+ tags = [t for t in offering.get("tags", []) if not t.startswith(prefixes)]
805
+ tags.extend(
806
+ "cap:" + name
807
+ for name, f in offering.get("features", {}).items()
808
+ if f.get("status") == "supported"
809
+ )
810
+ for direction in ["input", "output"]:
811
+ tags.extend(
812
+ direction + ":" + m
813
+ for m in offering.get("modalities", {}).get(direction, []) or []
814
+ )
815
+ if model.get("openWeights") is True:
816
+ tags.append("weights:open")
817
+ if model.get("origin"):
818
+ tags.append("origin:" + model["origin"])
819
+ tags.append("deployment:" + offering["deployment"])
820
+ ctx = offering.get("limits", {}).get("context")
821
+ if positive(ctx):
822
+ tags.append(
823
+ "context:"
824
+ + (
825
+ "1m+"
826
+ if ctx >= 1000000
827
+ else "128k+"
828
+ if ctx >= 128000
829
+ else "32k+"
830
+ if ctx >= 32000
831
+ else "under-32k"
832
+ )
833
+ )
834
+ p = offering.get("price", {})
835
+ if (
836
+ p.get("currency") == "USD"
837
+ and finite(p.get("input"))
838
+ and finite(p.get("output"))
839
+ and not p.get("tiers")
840
+ ):
841
+ cost = (p["input"] + p["output"]) / 1000
842
+ tags.append(
843
+ "price:"
844
+ + (
845
+ "zero-token-rate"
846
+ if cost == 0
847
+ else "budget"
848
+ if cost <= 0.002
849
+ else "mid"
850
+ if cost <= 0.02
851
+ else "premium"
852
+ )
853
+ )
854
+ return sorted(set(tags))
855
+
856
+
857
+ async def preflight(
858
+ offering,
859
+ messages,
860
+ *,
861
+ count_tokens=None,
862
+ output_tokens=0,
863
+ overhead_tokens=0,
864
+ options=None,
865
+ ):
866
+ if count_tokens is None:
867
+ return {
868
+ "status": "unknown",
869
+ "certainty": "unknown",
870
+ "diagnostics": [
871
+ diagnostic(
872
+ "TOKEN_COUNT_UNKNOWN",
873
+ "Supply a provider-aware token counter for actual requests.",
874
+ )
875
+ ],
876
+ }
877
+ import inspect
878
+
879
+ count = count_tokens(messages, offering)
880
+ if inspect.isawaitable(count):
881
+ count = await count
882
+ return check_fit(
883
+ offering,
884
+ {
885
+ "inputTokens": count["tokens"],
886
+ "outputTokens": output_tokens,
887
+ "overheadTokens": overhead_tokens,
888
+ },
889
+ {
890
+ **(options or {}),
891
+ "estimate": count.get("certainty") != "measured",
892
+ "certainty": count.get("certainty", "estimated"),
893
+ },
894
+ )
895
+
896
+
897
+ def _timestamp(value):
898
+ from datetime import datetime
899
+
900
+ parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
901
+ if parsed.tzinfo is None:
902
+ raise ValueError("Timezone required")
903
+ return parsed.timestamp()
904
+
905
+
906
+ def validate_verifications(records, data):
907
+ import re
908
+ from urllib.parse import urlparse
909
+
910
+ if not isinstance(records, list):
911
+ return ["verifications must be an array"]
912
+ errors = []
913
+ ids = set()
914
+ offerings = {o["id"] for o in data["offerings"]}
915
+ for r in records:
916
+ if not isinstance(r, dict):
917
+ errors.append("Invalid verification record")
918
+ continue
919
+ if not isinstance(r.get("id"), str) or not r["id"] or r["id"] in ids:
920
+ errors.append("Invalid or duplicate verification ID")
921
+ ids.add(r.get("id"))
922
+ if r.get("offeringId") not in offerings:
923
+ errors.append("Verification target is missing")
924
+ if not isinstance(r.get("contributor"), str) or not re.fullmatch(
925
+ r"github:[a-z0-9-]+", r["contributor"], re.I
926
+ ):
927
+ errors.append("Invalid verification contributor")
928
+ try:
929
+ if not re.fullmatch(r"\d{4}-\d{2}-\d{2}T.*Z", r.get("checkedAt", "")):
930
+ raise ValueError("Invalid date")
931
+ _timestamp(r["checkedAt"])
932
+ except (ValueError, TypeError):
933
+ errors.append("Invalid verification timestamp")
934
+ if r.get("result") not in ["passed", "failed"]:
935
+ errors.append("Invalid verification result")
936
+ if (
937
+ not isinstance(r.get("checks"), list)
938
+ or not r["checks"]
939
+ or any(c not in {m.value for m in VerificationCheck} for c in r["checks"])
940
+ ):
941
+ errors.append("Verification must name its checks")
942
+ try:
943
+ u = urlparse(r.get("evidence", ""))
944
+ if u.scheme != "https" or not u.hostname or u.username or u.password:
945
+ raise ValueError("Invalid URL")
946
+ except (ValueError, TypeError):
947
+ errors.append("Verification evidence is required")
948
+ if not isinstance(r.get("adapter"), str) or not r["adapter"]:
949
+ errors.append("Verification adapter and version are required")
950
+ if set(r) - {
951
+ "id",
952
+ "offeringId",
953
+ "contributor",
954
+ "checkedAt",
955
+ "result",
956
+ "checks",
957
+ "evidence",
958
+ "adapter",
959
+ "notes",
960
+ }:
961
+ errors.append(
962
+ "Unknown verification field; do not record keys or request payloads"
963
+ )
964
+ return errors
965
+
966
+
967
+ def verification_summary(
968
+ data, offering_id, *, as_of=None, max_age_days=90, contributor=None
969
+ ):
970
+ from datetime import datetime, timezone
971
+
972
+ now = _timestamp(as_of) if as_of else datetime.now(timezone.utc).timestamp()
973
+ records = [
974
+ r
975
+ for r in data.get("verifications", [])
976
+ if r["offeringId"] == offering_id
977
+ and (not contributor or r["contributor"].lower() == contributor.lower())
978
+ ]
979
+ calls = sorted(
980
+ (
981
+ r
982
+ for r in records
983
+ if "textGeneration" in r["checks"] and _timestamp(r["checkedAt"]) <= now
984
+ ),
985
+ key=lambda r: (
986
+ -_timestamp(r["checkedAt"]),
987
+ 0 if r["result"] == "failed" else 1,
988
+ r["id"],
989
+ ),
990
+ )
991
+ latest = calls[0] if calls else None
992
+ status = (
993
+ "unverified"
994
+ if latest is None
995
+ else "stale"
996
+ if now - _timestamp(latest["checkedAt"]) > max_age_days * 86400
997
+ else latest["result"]
998
+ )
999
+ return {"status": status, "latest": latest, "records": records}
1000
+
1001
+
1002
+ def matches_verification(data, oid, config):
1003
+ f = config.get("filters", {})
1004
+ if not any(k in f for k in ["verified", "verifiedBy", "verifiedWithinDays"]):
1005
+ return True
1006
+ status = verification_summary(
1007
+ data,
1008
+ oid,
1009
+ as_of=config.get("asOf"),
1010
+ max_age_days=f.get("verifiedWithinDays", 90),
1011
+ contributor=f.get("verifiedBy"),
1012
+ )["status"]
1013
+ return status != "passed" if f.get("verified") is False else status == "passed"
1014
+
1015
+
1016
+ def get_models(
1017
+ config: "PickerConfig | ModelList | None" = None,
1018
+ options: "PickerConfig | None" = None,
1019
+ ) -> list[dict]:
1020
+ return load_catalog().get_models(config, options)
1021
+
1022
+
1023
+ from .types import (
1024
+ PickerConfig,
1025
+ ContributorId,
1026
+ CustomTag,
1027
+ CustomProviderId,
1028
+ CustomCountryCode,
1029
+ CustomFileFormat,
1030
+ )
1031
+
1032
+
1033
+ def github_contributor(handle: str) -> ContributorId:
1034
+ import re
1035
+
1036
+ if not re.fullmatch(r"[a-z0-9-]{1,39}", handle, re.I):
1037
+ raise ValueError("Invalid GitHub handle")
1038
+ return ContributorId("github:" + handle)
1039
+
1040
+
1041
+ def custom_tag(value: str) -> CustomTag:
1042
+ import re
1043
+
1044
+ if not re.fullmatch(r"[a-z][a-z0-9_-]*:[^\s]+", value):
1045
+ raise ValueError("Use a namespaced tag")
1046
+ return CustomTag(value)
1047
+
1048
+
1049
+ def custom_provider_id(value: str) -> CustomProviderId:
1050
+ import re
1051
+
1052
+ if not re.fullmatch(r"[a-z][a-z0-9_-]{0,63}", value):
1053
+ raise ValueError("Invalid custom provider ID")
1054
+ return CustomProviderId(value)
1055
+
1056
+
1057
+ def custom_country_code(value: str) -> CustomCountryCode:
1058
+ import re
1059
+
1060
+ if not re.fullmatch(r"[a-z]{2}", value):
1061
+ raise ValueError("Use a two-letter country code")
1062
+ return CustomCountryCode(value)
1063
+
1064
+
1065
+ def custom_file_format(value: str) -> CustomFileFormat:
1066
+ import re
1067
+
1068
+ if not re.fullmatch(r"[a-z0-9][a-z0-9.+-]{0,63}", value):
1069
+ raise ValueError("Invalid custom file format")
1070
+ return CustomFileFormat(value)
1071
+
1072
+
1073
+ def _signal_date(value):
1074
+ import re
1075
+
1076
+ try:
1077
+ return (
1078
+ isinstance(value, str)
1079
+ and bool(re.fullmatch(r"\d{4}-\d{2}-\d{2}T.*Z", value))
1080
+ and math.isfinite(_timestamp(value))
1081
+ )
1082
+ except (ValueError, TypeError):
1083
+ return False
1084
+
1085
+
1086
+ def _validate_signals(o):
1087
+ from urllib.parse import urlparse
1088
+
1089
+ errors = []
1090
+
1091
+ def source(v):
1092
+ try:
1093
+ return (
1094
+ isinstance(v, str)
1095
+ and urlparse(v).scheme == "https"
1096
+ and bool(urlparse(v).netloc)
1097
+ )
1098
+ except ValueError:
1099
+ return False
1100
+
1101
+ def record(v, keys):
1102
+ return isinstance(v, dict) and set(v) <= set(keys)
1103
+
1104
+ l = o.get("lifecycleEvidence")
1105
+ p = o.get("popularityEvidence")
1106
+ if "lifecycleEvidence" in o and (
1107
+ not record(l, ["status", "source", "checkedAt", "basis"])
1108
+ or l.get("status") not in {v.value for v in Lifecycle}
1109
+ or ("basis" in l and l["basis"] not in {v.value for v in LifecycleBasis})
1110
+ or not source(l.get("source"))
1111
+ or not _signal_date(l.get("checkedAt"))
1112
+ ):
1113
+ errors.append(o["id"] + ": invalid lifecycle evidence")
1114
+ if "popularityEvidence" in o and (
1115
+ not record(
1116
+ p,
1117
+ [
1118
+ "metric",
1119
+ "rank",
1120
+ "population",
1121
+ "scope",
1122
+ "source",
1123
+ "windowStart",
1124
+ "windowEnd",
1125
+ ],
1126
+ )
1127
+ or p.get("metric") not in {v.value for v in PopularityMetric}
1128
+ or type(p.get("rank")) is not int
1129
+ or p["rank"] < 1
1130
+ or type(p.get("population")) is not int
1131
+ or p["population"] < p["rank"]
1132
+ or not isinstance(p.get("scope"), str)
1133
+ or not p["scope"].strip()
1134
+ or not source(p.get("source"))
1135
+ or not _signal_date(p.get("windowStart"))
1136
+ or not _signal_date(p.get("windowEnd"))
1137
+ or _timestamp(p["windowStart"]) >= _timestamp(p["windowEnd"])
1138
+ ):
1139
+ errors.append(o["id"] + ": invalid popularity evidence")
1140
+ return errors
1141
+
1142
+
1143
+ def _with_signals(o, as_of=None):
1144
+ import time
1145
+
1146
+ now = _timestamp(as_of) if as_of else time.time()
1147
+
1148
+ def fresh(date, days):
1149
+ return _signal_date(date) and 0 <= now - _timestamp(date) <= days * 86400
1150
+
1151
+ l = o.get("lifecycleEvidence")
1152
+ p = o.get("popularityEvidence")
1153
+ lifecycle = {
1154
+ "status": ("OUTDATED" if o.get("status") == "deprecated" else l["status"])
1155
+ if l and fresh(l["checkedAt"], 90)
1156
+ else "UNCERTAIN",
1157
+ "evidence": l,
1158
+ }
1159
+ popularity = {
1160
+ "status": (
1161
+ "POPULAR"
1162
+ if p["rank"] <= math.ceil(p["population"] * 0.1)
1163
+ else "NOT_POPULAR"
1164
+ )
1165
+ if p and fresh(p["windowEnd"], 30)
1166
+ else "UNCERTAIN",
1167
+ "evidence": p,
1168
+ }
1169
+ tags = [
1170
+ t for t in o.get("tags", []) if not t.startswith(("lifecycle:", "popularity:"))
1171
+ ]
1172
+ return dict(
1173
+ o,
1174
+ lifecycle=lifecycle,
1175
+ popularity=popularity,
1176
+ tags=tags
1177
+ + ["lifecycle:" + lifecycle["status"], "popularity:" + popularity["status"]],
1178
+ )
1179
+
1180
+
1181
+ def _normalize_query(input=None, options=None):
1182
+ if isinstance(input, str):
1183
+ if input not in {v.value for v in ModelList}:
1184
+ raise ValueError("Unknown model list")
1185
+ config = dict(options or {}, list=input)
1186
+ else:
1187
+ if input is not None and not isinstance(input, dict) or options is not None:
1188
+ raise ValueError("Use get_models(options) or get_models(list, options)")
1189
+ config = dict(input or {})
1190
+ if not any(k in config for k in ["list", "preset", "providerIds"]):
1191
+ config["list"] = ModelList.DEFAULT
1192
+ if config.get("list") == ModelList.CHINA:
1193
+ config["preset"] = "china"
1194
+ if config.get("list") == ModelList.GLOBAL:
1195
+ config["preset"] = "global"
1196
+ return config
1197
+
1198
+
1199
+ def _validate_local(entry):
1200
+ from urllib.parse import urlparse
1201
+
1202
+ if not isinstance(entry, dict) or set(entry) - {
1203
+ "endpoint",
1204
+ "api",
1205
+ "modelId",
1206
+ "name",
1207
+ }:
1208
+ raise ValueError("Invalid local entry")
1209
+ try:
1210
+ url = urlparse(entry.get("endpoint", ""))
1211
+ if (
1212
+ not isinstance(entry.get("endpoint"), str)
1213
+ or url.scheme not in ["http", "https"]
1214
+ or not url.netloc
1215
+ or url.username
1216
+ or url.password
1217
+ or url.query
1218
+ or url.fragment
1219
+ ):
1220
+ raise ValueError(
1221
+ "Use a local HTTP(S) URL without credentials, query, or fragment"
1222
+ )
1223
+ except (ValueError, TypeError):
1224
+ raise ValueError("Local entry needs an HTTP(S) endpoint")
1225
+ if "api" in entry and entry["api"] not in {v.value for v in LocalApi}:
1226
+ raise ValueError("Unknown local API")
1227
+ for key in ["modelId", "name"]:
1228
+ if key in entry and (not isinstance(entry[key], str) or not entry[key].strip()):
1229
+ raise ValueError("Invalid local " + key)
1230
+
1231
+
1232
+ def _local_groups(config, catalog):
1233
+ if (
1234
+ config.get("local") is False
1235
+ or "deployment" in config.get("filters", {})
1236
+ and "local" not in config["filters"]["deployment"]
1237
+ ):
1238
+ return []
1239
+ preset = next(
1240
+ (p for p in catalog.data["presets"] if p["id"] == config.get("preset")), None
1241
+ )
1242
+ ids = config.get("providerIds", preset["providerIds"] if preset else None)
1243
+ if (
1244
+ "local" not in config
1245
+ and ids is not None
1246
+ and not any(catalog.providers[id]["deployment"] == "local" for id in ids)
1247
+ ):
1248
+ return []
1249
+ entries = config.get("local", [{}])
1250
+ if not entries:
1251
+ return []
1252
+ provider = dict(
1253
+ catalog.providers.get(
1254
+ "local",
1255
+ {"id": "local", "auth": "optional", "deployment": "local", "tags": []},
1256
+ ),
1257
+ name="Local",
1258
+ )
1259
+ offerings = []
1260
+ for i, entry in enumerate(entries):
1261
+ offerings.append(
1262
+ {
1263
+ "id": "local/custom"
1264
+ if len(entries) == 1
1265
+ else "local/endpoint-" + str(i),
1266
+ "providerId": "local",
1267
+ "modelId": entry.get("modelId", "local/custom"),
1268
+ "apiModelId": entry.get("modelId", ""),
1269
+ "name": entry.get(
1270
+ "name", entry.get("modelId", "Local — enter your server URL")
1271
+ ),
1272
+ "deployment": "local",
1273
+ "local": {
1274
+ "endpoint": entry.get("endpoint", ""),
1275
+ "api": entry.get("api", "openai-compatible"),
1276
+ "configured": bool(entry.get("endpoint")),
1277
+ },
1278
+ "modalities": {"input": None, "output": None},
1279
+ "limits": {
1280
+ "context": None,
1281
+ "input": None,
1282
+ "output": None,
1283
+ "contextAccounting": "unknown",
1284
+ },
1285
+ "features": {},
1286
+ "price": {"currency": "USD", "input": None, "output": None},
1287
+ "tags": ["deployment:local"],
1288
+ "lifecycle": {"status": "UNCERTAIN", "evidence": None},
1289
+ "popularity": {"status": "UNCERTAIN", "evidence": None},
1290
+ }
1291
+ )
1292
+ return [{"provider": provider, "offerings": offerings}]
1293
+
1294
+
1295
+ from .inference import call, call_claude, prepare_call, connection_for, CallError
1296
+
1297
+ from .sdk import Byokit, OpenAI, Anthropic, openAI, claude
1298
+
1299
+ from .custom_model import append_model, validate_connection