mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
fix: preserve tracing for bounded activity queries
This commit is contained in:
parent
d8db99cc98
commit
98539210c8
2 changed files with 50 additions and 5 deletions
|
|
@ -67,12 +67,16 @@ class BoundedDailyActivityRepository:
|
|||
async def aggregate(
|
||||
self, scope: DailyActivityScope, *, model_limit: int | None = None
|
||||
) -> BoundedDailyActivityResponse:
|
||||
from litellm.proxy.db.db_span import db_span
|
||||
from litellm.proxy.db.prisma_query_span import sql_relation
|
||||
|
||||
if model_limit is not None and not 1 <= model_limit <= 100:
|
||||
raise ValueError("model_limit must be between 1 and 100")
|
||||
query: Final = build_bounded_daily_activity_sql(scope, model_limit=model_limit)
|
||||
rows: Final = TypeAdapter(tuple[_ActivityRow, ...]).validate_python(
|
||||
await self.database.query_raw(query.sql, *query.params)
|
||||
)
|
||||
async with db_span("daily_activity_query", sql_relation(query.sql)):
|
||||
rows: Final = TypeAdapter(tuple[_ActivityRow, ...]).validate_python(
|
||||
await self.database.query_raw(query.sql, *query.params)
|
||||
)
|
||||
total: Final = next(row for row in rows if row.kind == "total")
|
||||
other: Final = next((row for row in rows if row.kind == "other"), None)
|
||||
days: Final = tuple(
|
||||
|
|
|
|||
|
|
@ -284,7 +284,10 @@ def _parameters(parents: tuple[ast.AST, ...]) -> frozenset[str]:
|
|||
|
||||
|
||||
def _argument_for(call: ast.Call, function: ast.AsyncFunctionDef | ast.FunctionDef, parameter: str) -> ast.expr | None:
|
||||
positional: Final = tuple(arg.arg for arg in function.args.args)
|
||||
bound: Final = (
|
||||
isinstance(call.func, ast.Attribute) and bool(function.args.args) and function.args.args[0].arg == "self"
|
||||
)
|
||||
positional: Final = tuple(arg.arg for arg in function.args.args[1 if bound else 0 :])
|
||||
by_keyword: Final = next((k.value for k in call.keywords if k.arg == parameter), None)
|
||||
if by_keyword is not None or parameter not in positional:
|
||||
return by_keyword
|
||||
|
|
@ -324,7 +327,16 @@ def _caller_site(
|
|||
wrapped: Final = _wrapper_site(call, parents, method, location, module)
|
||||
if wrapped is not None:
|
||||
return wrapped
|
||||
text: Final = _sql_text(_argument_for(call, function, parameter), _scope(module, parents))
|
||||
argument: Final = _argument_for(call, function, parameter)
|
||||
caller: Final = next((p for p in parents if isinstance(p, (ast.AsyncFunctionDef, ast.FunctionDef))), None)
|
||||
if (
|
||||
caller is not None
|
||||
and caller.name in _RAW_METHODS
|
||||
and isinstance(argument, ast.Name)
|
||||
and argument.id in _parameters(parents)
|
||||
):
|
||||
return _PrismaCallSite(location, method, "forwarder", f"(callers of {caller.name})")
|
||||
text: Final = _sql_text(argument, _scope(module, parents))
|
||||
return _PrismaCallSite(location, method, "engine", _render_statements(method, text) if text is not None else None)
|
||||
|
||||
|
||||
|
|
@ -449,6 +461,35 @@ def prisma_call_sites() -> tuple[_PrismaCallSite, ...]:
|
|||
return tuple(site for module in _modules() for site in _call_sites(module)) # comprehension-ok: flatten
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("caller", "expression", "expected"),
|
||||
[
|
||||
("query_raw", "query", "(callers of query_raw)"),
|
||||
("query_raw", "query=query", "(callers of query_raw)"),
|
||||
("run", "'SELECT * FROM LiteLLM_UserTable'", "postgres.select LiteLLM_UserTable"),
|
||||
("run", "'not a SQL statement'", None),
|
||||
("run", "query", None),
|
||||
],
|
||||
)
|
||||
def test_query_wrapper_keeps_literal_and_forwarded_statements_checked(
|
||||
caller: str, expression: str, expected: str | None
|
||||
) -> None:
|
||||
tree: Final = ast.parse(
|
||||
f"""
|
||||
class Database:
|
||||
async def {caller}(self, query):
|
||||
return await self._execute({expression})
|
||||
|
||||
async def _execute(self, query):
|
||||
return await engine.query_raw(query)
|
||||
"""
|
||||
)
|
||||
module: Final = _Module(_REPO / "litellm" / "query_wrapper.py", tree, _assignments(tree.body))
|
||||
sites: Final = tuple(_call_sites(module))
|
||||
assert len(sites) == 1
|
||||
assert sites[0].rendered == expected
|
||||
|
||||
|
||||
def test_every_prisma_call_site_in_the_proxy_renders_a_bounded_postgres_span_name() -> None:
|
||||
"""A raw ``query_raw``/``execute_raw``/``query_first`` or a direct model call that no producer
|
||||
wraps is named by the engine from its payload; this scan replays that naming (and the wrappers')
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue