Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
4b08d02
feat(frontend): open New Evaluation and Add to test set from the /m p…
Nov 4, 2026
1abe457
feat(frontend): preview seven new /m developer views
Nov 4, 2026
60ab505
fix(frontend): keep the playground URL when an evaluator drawer closes
Nov 4, 2026
2cd6924
fix(frontend): warn before leaving the evaluator playground with unsa…
Nov 4, 2026
015b01e
fix(frontend): stop the update loop in playground compare mode
Nov 4, 2026
cf46e8f
fix(frontend): add every repeat, open no partial sheet, and wait for …
Nov 4, 2026
12e03c7
fix(frontend): open prompts row dialogs after the row menu closes
Nov 4, 2026
df88605
fix(frontend): Retry of add to queue skips traces already queued
Nov 4, 2026
c3a9d62
perf(frontend): read the run traces in the playground route, so the a…
Nov 4, 2026
6ee4a6e
test(frontend): wait for the test set option and use time based waits…
Nov 4, 2026
a9fbf38
fix(api): apply live evaluation trace filters instead of dropping them
Nov 4, 2026
b48691d
fix(api): parse a has_annotation row the same way twice
Nov 4, 2026
9f1bba8
fix(api): match the traces page for annotation filters and answer 400…
Nov 4, 2026
9b61f30
fix(frontend): run web unit test packages one at a time and wait for …
Nov 5, 2026
8646f4e
fix(frontend): wait for the debounced address write in the compare test
Nov 5, 2026
8ed74b1
fix(api): answer 400 for an unknown filter field only when a query is…
Nov 5, 2026
470c4ea
fix(frontend): drop the edited chat conversation on Discard & disconn…
Nov 5, 2026
0644f15
fix(api): skip live query traces without a root span id so evaluator …
Nov 5, 2026
2c3e4ad
feat(frontend): beta developer mode that stays in /m
Nov 5, 2026
92b5f08
fix(frontend): keep the /m playground bar buttons apart on a phone
Nov 5, 2026
66f7a58
fix(frontend): show the run duration in milliseconds, its real unit
Nov 5, 2026
5bae39a
fix(frontend): add the matching traces to a queue created from the ad…
Nov 5, 2026
47d3a4b
feat(frontend): show the beta developer mode switch in /w too
Nov 5, 2026
55abe83
fix(frontend): give a new test set row a field for each column
Nov 5, 2026
e1dd776
fix(frontend): show a saved trace annotation in the drawer without a …
Nov 5, 2026
3f8a594
v0.128.8
mmabrouk Nov 5, 2026
a0b4d96
fix(api): reject saved query filters the SQL builder cannot apply
mmabrouk Nov 5, 2026
1394eb0
Merge pull request #8116 from Agenta-AI/fix/web-unit-tests-main
mmabrouk Nov 5, 2026
ce3c478
Merge pull request #8108 from Agenta-AI/fix/w-bugs-from-live-qa
mmabrouk Nov 5, 2026
5fe9226
Merge pull request #8107 from Agenta-AI/feat/m-views-preview-batch1
mmabrouk Nov 5, 2026
c29ed51
Merge pull request #8121 from Agenta-AI/fix/api-live-eval-evaluator-t…
mmabrouk Nov 5, 2026
10b38ad
Merge pull request #8122 from Agenta-AI/fix/m-playground-before-preview
mmabrouk Nov 5, 2026
5475634
fix(frontend): hide Add to queue for an unsaved test set row in the p…
mmabrouk Nov 5, 2026
fddc3d9
fix(frontend): keep the Settings tab and the history entry when beta …
mmabrouk Nov 5, 2026
e3b3be0
fix(api): reject null btwn bounds and dict operators in saved annotat…
mmabrouk Nov 5, 2026
d63692d
Merge pull request #8110 from Agenta-AI/feat/m-playground-handoff-links
mmabrouk Nov 5, 2026
47270c0
Merge pull request #8123 from Agenta-AI/fix/m-observability-before-pr…
mmabrouk Nov 5, 2026
5d816e7
Merge pull request #8109 from Agenta-AI/fix/live-evaluation-filters
mmabrouk Nov 5, 2026
7afeed2
Merge pull request #8120 from Agenta-AI/feat/m-beta-developer-mode
mmabrouk Nov 5, 2026
cb378be
fix(frontend): show the trace and the step errors of a live evaluatio…
Nov 5, 2026
4c94d42
fix(frontend): count the errors of a live evaluation in the run overview
Nov 5, 2026
430b9a3
fix(frontend): show a paused live evaluation as Paused in /m
Nov 5, 2026
6931a31
fix(frontend): keep the create queue sheet open on Escape in the eval…
Nov 5, 2026
c8e0971
fix(frontend): drop a new test set row on Cancel and hide its dedup id
Nov 5, 2026
4f14d08
fix(frontend): reopen the trace drawer after a reload in /m
Nov 5, 2026
00c1e0a
fix(frontend): name the author in every Created by column in /m
Nov 5, 2026
2d467f9
fix(frontend): lock the new queue's type to the picker's items
Nov 5, 2026
f368a28
fix(frontend): stop a stuck annotation read from blocking the save
Nov 5, 2026
ef01006
fix(frontend): give a new test set row a field for each nested column
Nov 5, 2026
92e1fb6
fix(frontend): keep beta developer mode users in /m from /w addresses
mmabrouk Nov 5, 2026
ca1bf74
fix(frontend): send a beta developer mode user to /m on the first /w …
mmabrouk Nov 5, 2026
6016587
fix(frontend): show every failed step's error in a live scenario
Nov 5, 2026
999a48f
fix(frontend): build new test set rows safely and keep internal ids
Nov 5, 2026
1ca678c
fix(frontend): keep the trace drawer and the address in step on Back
Nov 5, 2026
9db66d0
fix(frontend): add up live evaluation errors fully and read Paused on…
Nov 5, 2026
55b8b33
fix(frontend): mark the signed-in author with (you) by profile id
Nov 5, 2026
65e8d7c
fix(frontend): do not report an annotation save as done before a read…
Nov 5, 2026
f8fec52
fix(frontend): keep the buy credits action when /w settings maps to /m
mmabrouk Nov 5, 2026
22711a9
Merge pull request #8125 from Agenta-AI/fix/m-views-qa-bugs
mmabrouk Nov 5, 2026
b7bc43c
Merge pull request #8126 from Agenta-AI/fix/beta-mode-routing
mmabrouk Nov 5, 2026
bd829aa
fix(frontend): match typed input keys in live evaluation filters
mmabrouk Nov 5, 2026
c55b7e0
fix(frontend): keep unconfirmed annotation writes after the sheet closes
Nov 5, 2026
533975b
fix(frontend): keep a __proto__ field typed in the test set drawer
Nov 5, 2026
886f40e
fix(frontend): keep dotted keys whole in the nested columns of a new row
Nov 5, 2026
f7ecc3e
fix(frontend): keep the trace drawer address right across traces and …
Nov 5, 2026
98ea5b8
Merge pull request #8128 from Agenta-AI/fix/live-eval-typed-input-key
mmabrouk Nov 5, 2026
0d07208
fix(frontend): show the beta developer mode switch only when AGENTA_B…
Nov 5, 2026
8c97af4
fix(frontend): show the filter message only for filter errors in the …
mmabrouk Nov 5, 2026
b9aac2c
fix(frontend): keep annotation writes from the moment they land
Nov 5, 2026
e2e6c66
fix(frontend): keep the ancestors in a column path read below the row
Nov 5, 2026
1b05afc
fix(frontend): keep Forward after Back clears the trace drawer span
Nov 5, 2026
aee8b66
style(frontend): format the beta developer mode tests
Nov 5, 2026
794a118
Merge pull request #8131 from Agenta-AI/fix/live-eval-drawer-error-text
mmabrouk Nov 5, 2026
1dd5892
fix(frontend): take a column walk's ancestors as keys, not a dotted s…
Nov 5, 2026
050726d
fix(frontend): replace the history entry only when the drawer falls back
Nov 5, 2026
7b6fc81
Merge pull request #8129 from Agenta-AI/fix/beta-developer-mode-env
mmabrouk Nov 5, 2026
07c2816
Merge pull request #8130 from Agenta-AI/fix/m-views-qa-bugs-followup
mmabrouk Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions api/oss/src/apis/fastapi/queries/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from functools import wraps

from fastapi import HTTPException

from oss.src.core.tracing.dtos import FilteringException


def handle_filtering_exceptions():
"""Answer 400 when a saved query holds a filter that cannot be applied."""

def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
try:
return await func(*args, **kwargs)
except FilteringException as e:
raise HTTPException(status_code=400, detail=str(e)) from e

return wrapper

return decorator
6 changes: 6 additions & 0 deletions api/oss/src/apis/fastapi/queries/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)
from oss.src.core.git.utils import build_retrieval_info
from oss.src.core.queries.dtos import QueryRevision, QueryRevisionCommit
from oss.src.apis.fastapi.queries.exceptions import handle_filtering_exceptions
from oss.src.core.queries.service import (
QueriesService,
SimpleQueriesService,
Expand Down Expand Up @@ -726,6 +727,7 @@ async def fork_query_variant(

@intercept_exceptions()
@handle_git_exceptions()
@handle_filtering_exceptions()
async def create_query_revision(
self,
request: Request,
Expand Down Expand Up @@ -798,6 +800,7 @@ async def fetch_query_revision(
return query_revision_response

@intercept_exceptions()
@handle_filtering_exceptions()
async def edit_query_revision(
self,
request: Request,
Expand Down Expand Up @@ -933,6 +936,7 @@ async def query_query_revisions(
return query_revisions_response

@intercept_exceptions()
@handle_filtering_exceptions()
async def commit_query_revision(
self,
request: Request,
Expand Down Expand Up @@ -1183,6 +1187,7 @@ def __init__(
# SIMPLE QUERIES -----------------------------------------------------------

@intercept_exceptions()
@handle_filtering_exceptions()
async def create_simple_query(
self,
request: Request,
Expand Down Expand Up @@ -1243,6 +1248,7 @@ async def fetch_simple_query(
return simple_query_response

@intercept_exceptions()
@handle_filtering_exceptions()
async def edit_simple_query(
self,
request: Request,
Expand Down
77 changes: 67 additions & 10 deletions api/oss/src/core/evaluations/runtime/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,40 @@
from oss.src.core.tracing.service import TracingService
from oss.src.core.shared.dtos import Reference
from oss.src.core.tracing.dtos import (
ComparisonOperator,
Condition,
Fields,
Filtering,
TraceType,
Windowing,
Formatting,
Format,
Focus,
TracingQuery,
LogicalOperator,
)
from oss.src.utils.logging import get_module_logger

log = get_module_logger(__name__)


def _only_invocations(filtering: Filtering) -> Filtering:
"""The live query filter under an outer AND on invocation traces.

A live run must never read evaluator (annotation) traces back, also when the
user's filter has an OR that matches them.
"""
return Filtering(
operator=LogicalOperator.AND,
conditions=[
Condition(
field=Fields.TRACE_TYPE.value,
operator=ComparisonOperator.IS,
value=TraceType.INVOCATION.value,
),
filtering,
],
)


def _extract_root_span(trace: Any) -> Optional[Any]:
Expand All @@ -32,11 +58,12 @@ def _extract_root_span(trace: Any) -> Optional[Any]:
if not isinstance(spans, dict) or not spans:
return None

for span in spans.values():
if isinstance(span, list):
continue
if _extract_span_id(span):
return span
# Same-name sibling roots arrive as a list. Any root carries the link that
# types the evaluator trace `annotation`, so a list must not leave it unlinked.
for entry in spans.values():
for span in entry if isinstance(entry, list) else [entry]:
if _extract_span_id(span):
return span

return None

Expand Down Expand Up @@ -97,6 +124,34 @@ def _trace_source_item(
)


def _linked_trace_source_items(
*,
traces: List[Any],
step_key: str,
) -> List[ResolvedSourceItem]:
"""Query source items for the traces that have a root span id.

The root span id becomes the evaluator's `invocation` link, and that link types
the evaluator trace `annotation`. A trace without one would give an evaluator
trace typed `invocation`, which the next live tick reads back as a new trace.
Such a trace also has no outputs to evaluate, so it is skipped.
"""
items: List[ResolvedSourceItem] = []
for trace in traces:
if not trace or not trace.trace_id:
continue
item = _trace_source_item(trace=trace, step_key=step_key)
if not item.span_id:
log.warning(
"[EVAL] Skipping a query trace with no root span id.",
trace_id=str(item.trace_id),
step_key=step_key,
)
continue
items.append(item)
return items


class SourceResolutionError(Exception):
"""An input step does not carry exactly one recognized source reference."""

Expand Down Expand Up @@ -487,6 +542,9 @@ async def resolve_live_query_traces(
elif query_windowing:
windowing.rate = query_windowing.rate

if not use_windowing:
filtering = _only_invocations(filtering)

query_traces[query_step_key] = (
await self.tracing_service.query_traces(
project_id=project_id,
Expand Down Expand Up @@ -552,10 +610,9 @@ async def resolve_query_source_items(
)

return {
query_step_key: [
_trace_source_item(trace=trace, step_key=query_step_key)
for trace in traces
if trace and trace.trace_id
]
query_step_key: _linked_trace_source_items(
traces=traces,
step_key=query_step_key,
)
for query_step_key, traces in query_traces.items()
}
18 changes: 16 additions & 2 deletions api/oss/src/core/evaluations/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
from oss.src.core.tracing.dtos import (
TracingQuery,
Filtering,
FilteringException,
Condition,
ListOperator,
MetricSpec,
Expand Down Expand Up @@ -297,7 +298,7 @@ async def refresh_runs(
run=run,
):
log.warning(
"[LIVE] Closing invalid live run (null data or non-invocation trace_type).",
"[LIVE] Closing invalid live run (null data, non-invocation trace_type, or a filter that cannot be applied).",
project_id=project_id,
run_id=run.id,
)
Expand Down Expand Up @@ -381,7 +382,8 @@ async def _is_live_run_valid(
project_id: UUID,
run: EvaluationRun,
) -> bool:
"""Every query step must reference a revision with data targeting invocation traces."""
"""Every query step must reference a revision with data targeting invocation
traces and a filter that the tracing query can apply."""
if not run.data or not run.data.steps:
return False

Expand All @@ -407,6 +409,18 @@ async def _is_live_run_valid(
if not _is_invocation_query(query_revision.data):
return False

try:
QueriesService.validate_query_revision_filtering(query_revision.data)
except FilteringException as e:
log.warning(
"[LIVE] Live query filter cannot be applied.",
project_id=project_id,
run_id=run.id,
query_revision_id=query_revision_id,
error=str(e),
)
return False

return True

async def _close_live_run(
Expand Down
17 changes: 17 additions & 0 deletions api/oss/src/core/queries/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Focus,
Format,
)
from oss.src.core.tracing.utils.filtering import validate_filtering
from oss.src.core.tracing.utils.parsing import (
parse_trace_id_from_uuid,
parse_span_id_from_uuid,
Expand Down Expand Up @@ -89,7 +90,17 @@ def __init__(
self.tracing_service = tracing_service

@staticmethod
def validate_query_revision_filtering(
data: Optional[QueryRevisionData],
) -> None:
"""Raise `FilteringException` for a filter the tracing query cannot apply,
also for an unknown field."""
if data is not None:
validate_filtering(data.filtering)

@classmethod
def _sanitize_persisted_query_revision_data(
cls,
data: Optional[Any],
) -> Optional[Dict[str, Any]]:
"""Persist only canonical query revision fields."""
Expand All @@ -101,6 +112,8 @@ def _sanitize_persisted_query_revision_data(
except Exception:
return None

cls.validate_query_revision_filtering(parsed)

persisted_data: Dict[str, Any] = {}
if parsed.formatting is not None:
persisted_data["formatting"] = parsed.formatting
Expand Down Expand Up @@ -1060,6 +1073,8 @@ async def create(
#
query_id: Optional[UUID] = None,
) -> Optional[SimpleQuery]:
QueriesService.validate_query_revision_filtering(simple_query_create.data)

# ----------------------------------------------------------------------
# Query
# ----------------------------------------------------------------------
Expand Down Expand Up @@ -1305,6 +1320,8 @@ async def edit(
if str(query_id) != str(simple_query_edit.id):
return None

QueriesService.validate_query_revision_filtering(simple_query_edit.data)

# ----------------------------------------------------------------------
# Query
# ----------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions api/oss/src/core/tracing/dtos.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class Fields(str, Enum):
SESSION_ID = "session_id"
USER_ID = "user_id"
AGENT_ID = "agent_id"
HAS_ANNOTATION = "has_annotation"


class LogicalOperator(str, Enum):
Expand Down
Loading
Loading