feat: allow filtering steps by feedback (#3061)
This commit is contained in:
@@ -183,6 +183,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
|
||||
identifier_keys: Optional[List[str]] = None,
|
||||
identity_id: Optional[str] = None,
|
||||
query_options: Sequence[ORMOption] | None = None, # ← new
|
||||
has_feedback: Optional[bool] = None,
|
||||
**kwargs,
|
||||
) -> List["SqlalchemyBase"]:
|
||||
"""
|
||||
@@ -281,6 +282,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
|
||||
identifier_keys: Optional[List[str]] = None,
|
||||
identity_id: Optional[str] = None,
|
||||
check_is_deleted: bool = False,
|
||||
has_feedback: Optional[bool] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@@ -337,6 +339,13 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
|
||||
if end_date:
|
||||
query = query.filter(cls.created_at < end_date)
|
||||
|
||||
# Feedback filtering
|
||||
if has_feedback is not None and hasattr(cls, "feedback"):
|
||||
if has_feedback:
|
||||
query = query.filter(cls.feedback.isnot(None))
|
||||
else:
|
||||
query = query.filter(cls.feedback.is_(None))
|
||||
|
||||
# Handle pagination based on before/after
|
||||
if before_obj or after_obj:
|
||||
conditions = []
|
||||
|
||||
@@ -24,6 +24,7 @@ async def list_steps(
|
||||
agent_id: Optional[str] = Query(None, description="Filter by the ID of the agent that performed the step"),
|
||||
trace_ids: Optional[list[str]] = Query(None, description="Filter by trace ids returned by the server"),
|
||||
feedback: Optional[Literal["positive", "negative"]] = Query(None, description="Filter by feedback"),
|
||||
has_feedback: Optional[bool] = Query(None, description="Filter by whether steps have feedback (true) or not (false)"),
|
||||
tags: Optional[list[str]] = Query(None, description="Filter by tags"),
|
||||
server: SyncServer = Depends(get_letta_server),
|
||||
actor_id: Optional[str] = Header(None, alias="user_id"),
|
||||
@@ -50,6 +51,7 @@ async def list_steps(
|
||||
agent_id=agent_id,
|
||||
trace_ids=trace_ids,
|
||||
feedback=feedback,
|
||||
has_feedback=has_feedback,
|
||||
tags=tags,
|
||||
)
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ class StepManager:
|
||||
agent_id: Optional[str] = None,
|
||||
trace_ids: Optional[list[str]] = None,
|
||||
feedback: Optional[Literal["positive", "negative"]] = None,
|
||||
has_feedback: Optional[bool] = None,
|
||||
) -> List[PydanticStep]:
|
||||
"""List all jobs with optional pagination and status filter."""
|
||||
async with db_registry.async_session() as session:
|
||||
@@ -61,6 +62,7 @@ class StepManager:
|
||||
end_date=end_date,
|
||||
limit=limit,
|
||||
ascending=True if order == "asc" else False,
|
||||
has_feedback=has_feedback,
|
||||
**filter_kwargs,
|
||||
)
|
||||
return [step.to_pydantic() for step in steps]
|
||||
|
||||
@@ -6200,6 +6200,19 @@ async def test_job_usage_stats_add_multiple(server: SyncServer, sarah_agent, def
|
||||
steps = await step_manager.list_steps_async(agent_id=sarah_agent.id, actor=default_user)
|
||||
assert len(steps) == 2
|
||||
|
||||
# add step feedback
|
||||
step_manager = server.step_manager
|
||||
|
||||
# Add feedback to first step
|
||||
await step_manager.add_feedback_async(step_id=steps[0].id, feedback="positive", actor=default_user)
|
||||
|
||||
# Test has_feedback filtering
|
||||
steps_with_feedback = await step_manager.list_steps_async(agent_id=sarah_agent.id, has_feedback=True, actor=default_user)
|
||||
assert len(steps_with_feedback) == 1
|
||||
|
||||
steps_without_feedback = await step_manager.list_steps_async(agent_id=sarah_agent.id, actor=default_user)
|
||||
assert len(steps_without_feedback) == 2
|
||||
|
||||
|
||||
def test_job_usage_stats_get_nonexistent_job(server: SyncServer, default_user):
|
||||
"""Test getting usage statistics for a nonexistent job."""
|
||||
|
||||
Reference in New Issue
Block a user