Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/list_api/in_memory.py: 33%
81 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:01 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:01 +0000
1"""An in-memory `ListExecutor`, for list resources whose rows are computed rather than queried.
3Answers the same `QueryPlan` a SQL executor would render through `where_sql` / `order_by_sql`,
4so a filter or a sort means the same thing on either. `enrich_page` runs on the page slice and
5never on the whole match set.
6"""
8from collections.abc import Awaitable, Callable, Mapping, Sequence
9from dataclasses import dataclass
10from datetime import datetime
11from functools import reduce
12from typing import Final, Generic, TypeAlias, TypeVar
14from typing_extensions import assert_never
16from litellm.proxy.list_api.list_framework import (
17 AnyOf,
18 Compare,
19 ComparisonOp,
20 FilterValue,
21 IsNull,
22 Predicate,
23 QueryPlan,
24 SortKey,
25 Within,
26)
28TRow: Final = TypeVar("TRow")
30Cell: TypeAlias = str | int | float | datetime | None
31# A tuple-valued cell is a row's repeated field (a model group's providers, say). A predicate
32# holds against it when it holds against any one element, the way an SQL join would answer.
33Cells: TypeAlias = Mapping[str, Cell | tuple[Cell, ...]]
36def _sign(cell: Cell, value: FilterValue) -> int | None:
37 """None when the two values are not orderable against each other."""
38 if isinstance(cell, str) and isinstance(value, str):
39 return (cell > value) - (cell < value)
40 if isinstance(cell, datetime) and isinstance(value, datetime):
41 return (cell > value) - (cell < value)
42 if isinstance(cell, (int, float)) and isinstance(value, (int, float)):
43 return (cell > value) - (cell < value)
44 return None
47def _matches(cell: Cell, op: ComparisonOp, value: FilterValue) -> bool:
48 """SQL's three-valued logic: a NULL cell satisfies no comparison, only `is_null`."""
49 if cell is None:
50 return False
51 sign: Final = _sign(cell, value)
52 match op:
53 case "eq":
54 return cell == value
55 case "not":
56 return cell != value
57 case "contains":
58 return str(value).casefold() in str(cell).casefold()
59 case "gt":
60 return sign is not None and sign > 0
61 case "gte":
62 return sign is not None and sign >= 0
63 case "lt":
64 return sign is not None and sign < 0
65 case "lte":
66 return sign is not None and sign <= 0
67 case _:
68 assert_never(op)
71def _any_cell(cells: Cells, name: str, matches: Callable[[Cell], bool]) -> bool:
72 cell: Final = cells.get(name)
73 if isinstance(cell, tuple):
74 return any(matches(item) for item in cell)
75 return matches(cell)
78def _leaf_holds(predicate: Compare | Within | IsNull, cells: Cells) -> bool:
79 match predicate:
80 case Compare(field=name, op=op, value=value):
81 return _any_cell(cells, name, lambda cell: _matches(cell, op, value))
82 case Within(field=name, values=values):
83 return _any_cell(cells, name, lambda cell: cell is not None and cell in values)
84 case IsNull(field=name, negated=negated):
85 return _any_cell(cells, name, lambda cell: (cell is None) != negated)
86 case _:
87 assert_never(predicate)
90def _holds(predicate: Predicate, cells: Cells) -> bool:
91 if isinstance(predicate, AnyOf):
92 return any(_leaf_holds(clause, cells) for clause in predicate.clauses)
93 return _leaf_holds(predicate, cells)
96def _sort_key(cells: Cells, key: SortKey) -> tuple[bool, Cell | tuple[Cell, ...]]:
97 """NULLS LAST in both directions, matching `order_by_sql`.
99 The placeholder standing in for a null is only ever compared against another null's,
100 because the rank ahead of it already separates nulls from the rest.
101 """
102 cell: Final = cells.get(key.field)
103 return (cell is None) != key.descending, 0 if cell is None else cell
106def _ordered(
107 matched: Sequence[tuple[Cells, TRow]],
108 order: tuple[SortKey, ...],
109) -> Sequence[tuple[Cells, TRow]]:
110 """Least significant key first: Python's sort is stable, so the most significant pass wins."""
111 return reduce(
112 lambda rows, key: sorted(rows, key=lambda pair: _sort_key(pair[0], key), reverse=key.descending),
113 reversed(order),
114 matched,
115 )
118async def _unchanged(rows: Sequence[TRow]) -> Sequence[TRow]:
119 return rows
122@dataclass(frozen=True, slots=True)
123class InMemoryListExecutor(Generic[TRow]):
124 """`cells` projects a row down to the values the spec's filters, search and sort read, so a
125 plan can be applied without this module knowing the row type."""
127 rows: Sequence[TRow]
128 cells: Callable[[TRow], Cells]
129 enrich_page: Callable[[Sequence[TRow]], Awaitable[Sequence[TRow]]] = _unchanged
131 def _matching(self, where: tuple[Predicate, ...]) -> Sequence[tuple[Cells, TRow]]:
132 return tuple(
133 (cells, row)
134 for cells, row in ((self.cells(row), row) for row in self.rows)
135 if all(_holds(predicate, cells) for predicate in where)
136 )
138 async def count(self, where: tuple[Predicate, ...]) -> int:
139 return len(self._matching(where))
141 async def find_many(self, plan: QueryPlan) -> Sequence[TRow]:
142 page: Final = _ordered(self._matching(plan.where), plan.order)[plan.skip : plan.skip + plan.take]
143 return await self.enrich_page(tuple(row for _, row in page))
145 async def distinct(self, field: str, where: tuple[Predicate, ...]) -> Sequence[str]:
146 """A repeated field contributes each of its elements, so a facet over `providers`
147 lists providers rather than the tuples rows happen to carry."""
148 cells: Final = (cells.get(field) for cells, _ in self._matching(where))
149 values: Final = (
150 value
151 for cell in cells
152 for value in (cell if isinstance(cell, tuple) else (cell,))
153 if isinstance(value, str) and value
154 )
155 return tuple(sorted(frozenset(values)))