Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
c96c99c
feat(frontend): playground share links, last selection and links in f…
Nov 3, 2026
c191dc2
test(frontend): stories for the playground links and the focus drawer…
Nov 3, 2026
4b2b88c
test(frontend): start each revision bar test without a remembered pla…
Nov 3, 2026
cd45dac
fix(frontend): address review round 1 of the playground links
Nov 3, 2026
7ed6168
fix(frontend): address review round 2 of the playground links
Nov 3, 2026
bdf33d3
fix(frontend): restore and keep the playground snapshot on a hash-onl…
Nov 3, 2026
7df76ba
fix(frontend): style the variants menu's playground link as a menu item
Nov 3, 2026
b3fa116
fix(frontend): fill the endpoints snippet inputs from the prompt vari…
Nov 3, 2026
5b5a3c5
fix(frontend): show a chat prompt as Chat in the /m prompts list
Nov 3, 2026
33bc7a2
fix(frontend): fix the /m variants deploy note, labels and Modified by
Nov 3, 2026
799deb7
fix(frontend): stop the React update-during-render warning on the /m …
Nov 3, 2026
a931cd1
fix(frontend): update the endpoints story to the new snippet input shape
Nov 3, 2026
daf88ee
fix(api): let a workflow edit move an item back to the top level
Nov 3, 2026
9d982b3
Merge remote-tracking branch 'origin/release/v0.128.5' into fix/m-hid…
Nov 4, 2026
b8e6883
chore(frontend): fix the import order in three /m playground tests
Nov 4, 2026
12477a5
Merge remote-tracking branch 'origin/release/v0.128.5' into feat/m-vi…
Nov 4, 2026
7f1a2c7
Merge remote-tracking branch 'origin/release/v0.128.5' into fix/m-hid…
Nov 4, 2026
1d7a73c
Merge remote-tracking branch 'origin/release/v0.128.5' into fix/workf…
Nov 4, 2026
2993194
fix(frontend): report a saved workflow update as a success when the r…
Nov 4, 2026
538338c
test(frontend): cover the no-write and commit paths of the updateWork…
Nov 4, 2026
672651f
fix(frontend): stop the queue scan on a failed trace read, retry only…
Nov 4, 2026
e02f9f1
test(frontend): type the annotation filter row in the live evaluation…
Nov 4, 2026
3d961eb
fix(frontend): keep the playground hash when the trace drawer opens it
Nov 4, 2026
65e6d97
fix(frontend): fit the run pane header on a phone, keep JSON text typ…
Nov 4, 2026
f76c78f
fix(frontend): refresh the queue after a failed scan that added trace…
Nov 4, 2026
e9a3220
perf(frontend): keep the traces page chunk as it was; the live filter…
Nov 4, 2026
ea2bed4
fix(frontend): live evaluation filter offers no annotation field the …
Nov 4, 2026
2672d0d
fix(frontend): drop a form write once another value replaces it
Nov 4, 2026
2b272db
fix(frontend): drop a form write when the JSON view removes its field
Nov 4, 2026
736d1d7
fix(frontend): edit the user's own trace annotation and save only the…
Nov 4, 2026
cc3c8e1
fix(api): give live evaluation evaluators the trace outputs and invoc…
Nov 4, 2026
5c8f0d6
feat(frontend): run a scenario's invocation from the /m evaluation ru…
Nov 4, 2026
76dd36c
test(frontend): describe the optional fields of the run annotate fixture
Nov 4, 2026
f49d3e4
fix(frontend): reset a failed scenario after a successful run and ski…
Nov 4, 2026
77a3d98
fix(frontend): reset the scenario before storing a successful retry
Nov 4, 2026
0550f9c
fix(frontend): store an app answer without a trace as a failed run
Nov 4, 2026
a92eddb
perf(frontend): load the playground runner only on the pages that nee…
Nov 4, 2026
f731dc8
perf(frontend): lower the /m speed budget to the new first-load sizes
Nov 4, 2026
1ba7d4c
v0.128.7
mmabrouk Nov 4, 2026
e36c696
Merge pull request #8101 from Agenta-AI/fix/live-evaluation-outputs
mmabrouk Nov 4, 2026
dfb6854
Merge pull request #8105 from Agenta-AI/perf/m-shell-playground-runner
mmabrouk Nov 4, 2026
71cdfcc
Merge pull request #8096 from Agenta-AI/fix/m-preview-app-overview
mmabrouk Nov 4, 2026
0c4a986
fix(frontend): keep a saved null annotation answer after the user set…
Nov 4, 2026
bc0c570
fix(frontend): say how many traces were queued when a page read stops…
Nov 4, 2026
4287595
fix(frontend): close the trace drawer after the playground page opens…
Nov 4, 2026
ad3d1d8
fix(frontend): pass authorNames in the DeploymentsTab story
Nov 4, 2026
194c0f1
Merge pull request #8102 from Agenta-AI/fix/m-annotate-save-bugs
mmabrouk Nov 4, 2026
226c143
Merge pull request #8098 from Agenta-AI/fix/m-preview-traces-actions
mmabrouk Nov 4, 2026
c9b31d2
Merge pull request #8099 from Agenta-AI/fix/m-preview-playground
mmabrouk Nov 4, 2026
2599de3
Merge pull request #8103 from Agenta-AI/feat/m-run-scenario-invocation
mmabrouk Nov 4, 2026
ba8f446
Merge pull request #8076 from Agenta-AI/fix/m-hidden-views-preview
mmabrouk Nov 4, 2026
6ceae22
Merge pull request #8077 from Agenta-AI/fix/workflow-move-to-root
mmabrouk Nov 4, 2026
2c9e273
Merge release/v0.128.7 into feat/m-view-playground-links
mmabrouk Nov 4, 2026
92343f5
Merge pull request #8075 from Agenta-AI/feat/m-view-playground-links
mmabrouk Nov 4, 2026
8b2db75
fix(frontend): show a plain message when the /w queue add fails
mmabrouk Nov 4, 2026
ccdb0cd
Merge pull request #8112 from Agenta-AI/fix/w-queue-add-plain-error
mmabrouk Nov 4, 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
7 changes: 7 additions & 0 deletions api/oss/src/core/applications/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,16 @@ async def edit_application(
#
application_edit: ApplicationEdit,
) -> Optional[Application]:
# Only a folder_id the caller sent may reach the workflow edit, where a null
# moves the application to the top level.
workflow_edit = WorkflowEdit(
**application_edit.model_dump(
mode="json",
exclude=(
None
if "folder_id" in application_edit.model_fields_set
else {"folder_id"}
),
),
)

Expand Down
58 changes: 35 additions & 23 deletions api/oss/src/core/evaluations/runtime/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,33 @@ def _extract_ag_data(trace: Any) -> Dict[str, Any]:
return data if isinstance(data, dict) else {}


def _trace_source_item(
*,
trace: Any,
step_key: str,
trace_id: Optional[str] = None,
) -> ResolvedSourceItem:
"""A trace source item with the root span id, inputs and outputs read from the trace.

The evaluator steps read the outputs from these fields, and the span id becomes the
evaluator's `invocation` link. Without the link the evaluator trace is typed
`invocation` and a live query on invocation traces reads it back.
"""
ag_data = _extract_ag_data(trace) if trace is not None else {}
root_span = _extract_root_span(trace) if trace is not None else None
return ResolvedSourceItem(
kind="trace",
step_key=step_key,
#
trace_id=trace_id or getattr(trace, "trace_id", None),
span_id=_extract_span_id(root_span),
trace=trace,
#
inputs=ag_data.get("inputs"),
outputs=ag_data.get("outputs"),
)


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

Expand Down Expand Up @@ -397,23 +424,14 @@ async def resolve_direct_source_items(
)
for testcase_id in testcase_ids
)
for trace_id in trace_ids:
trace = traces_by_id.get(trace_id)
ag_data = _extract_ag_data(trace) if trace is not None else {}
root_span = _extract_root_span(trace) if trace is not None else None
source_items.append(
ResolvedSourceItem(
kind="trace",
step_key="",
#
trace_id=trace_id,
span_id=_extract_span_id(root_span),
trace=trace,
#
inputs=ag_data.get("inputs"),
outputs=ag_data.get("outputs"),
)
source_items.extend(
_trace_source_item(
trace=traces_by_id.get(trace_id),
trace_id=trace_id,
step_key="",
)
for trace_id in trace_ids
)

return source_items

Expand Down Expand Up @@ -535,13 +553,7 @@ async def resolve_query_source_items(

return {
query_step_key: [
ResolvedSourceItem(
kind="trace",
step_key=query_step_key,
#
trace_id=trace.trace_id,
trace=trace,
)
_trace_source_item(trace=trace, step_key=query_step_key)
for trace in traces
if trace and trace.trace_id
]
Expand Down
7 changes: 7 additions & 0 deletions api/oss/src/core/evaluators/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,16 @@ async def edit_evaluator(
#
evaluator_edit: EvaluatorEdit,
) -> Optional[Evaluator]:
# Only a folder_id the caller sent may reach the workflow edit, where a null
# moves the evaluator to the top level.
workflow_edit = WorkflowEdit(
**evaluator_edit.model_dump(
mode="json",
exclude=(
None
if "folder_id" in evaluator_edit.model_fields_set
else {"folder_id"}
),
),
)

Expand Down
4 changes: 4 additions & 0 deletions api/oss/src/core/workflows/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,10 @@ async def edit_workflow(
exclude_none=True,
exclude={"flags"},
)
# exclude_none drops a null folder_id, but a null sent on purpose moves the
# workflow to the top level.
if "folder_id" in workflow_edit.model_fields_set:
artifact_edit_kwargs["folder_id"] = workflow_edit.folder_id
if "flags" in workflow_edit.model_fields_set:
artifact_flags = self._artifact_flags_from_any(workflow_edit.flags)
artifact_edit_kwargs["flags"] = (
Expand Down
77 changes: 77 additions & 0 deletions api/oss/tests/pytest/unit/evaluations/test_query_eval_loops.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,80 @@ async def test_run_query_source_live_run_not_finalized_on_error(monkeypatch):

# live runs are never finalized on error — the scheduler keeps polling.
edit_run.assert_not_awaited()


@pytest.mark.asyncio
async def test_query_source_items_carry_root_span_inputs_and_outputs():
"""A live query trace feeds the evaluator its outputs and an invocation link.

Without the outputs the code evaluator fails with "Invalid outputs". Without
the span id the evaluator trace has no links, so it is typed `invocation`
and the live query (`trace_type is invocation`) reads it back.
"""
from agenta.sdk.models.tracing import SpansNode, Trace
from oss.src.core.evaluations.tasks.processor import _seed_context_from_source

query_revision_id = uuid4()
trace_id = "a" * 32
span_id = "b" * 16
trace = Trace(
trace_id=trace_id,
spans={
"capital": SpansNode(
trace_id=trace_id,
span_id=span_id,
attributes={
"ag": {
"data": {
"inputs": {"country": "Peru"},
"outputs": "Lima",
}
}
},
)
},
)
run = EvaluationRun(
id=uuid4(),
principal=any_principal(),
flags=EvaluationRunFlags(is_live=True, has_queries=True),
data=EvaluationRunData(
steps=[
EvaluationRunDataStep(
key="query-live",
type="input",
origin="custom",
references={"query_revision": Reference(id=query_revision_id)},
),
],
repeats=1,
),
)
query_traces = AsyncMock(return_value=[trace])
sources = SourceResolution(
queries_service=SimpleNamespace(
fetch_query_revision=AsyncMock(
return_value=SimpleNamespace(
id=query_revision_id,
slug="query-live",
data=SimpleNamespace(filtering=None, windowing=None),
)
)
),
tracing_service=SimpleNamespace(query_traces=query_traces),
)

items = await sources.resolve_query_source_items(
project_id=uuid4(),
run=run,
)

[item] = items["query-live"]
assert item.trace_id == trace_id
assert item.span_id == span_id
assert item.inputs == {"country": "Peru"}
assert item.outputs == "Lima"

context = _seed_context_from_source(source_item=item, repeats=1)
assert context[0]["outputs"] == "Lima"
assert context[0]["span_id"] == span_id
104 changes: 104 additions & 0 deletions api/oss/tests/pytest/unit/workflows/test_edit_workflow_folder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from unittest.mock import AsyncMock
from uuid import uuid4

import pytest

import oss.src.core.workflows.service as workflows_service_module
from oss.src.core.applications.dtos import ApplicationEdit
from oss.src.core.applications.service import ApplicationsService
from oss.src.core.evaluators.dtos import EvaluatorEdit
from oss.src.core.evaluators.service import EvaluatorsService
from oss.src.apis.fastapi.workflows.models import WorkflowEditRequest
from oss.src.core.workflows.dtos import Workflow
from oss.src.core.workflows.service import WorkflowsService


@pytest.fixture
def service(monkeypatch):
monkeypatch.setattr(workflows_service_module, "get_cache", AsyncMock())
monkeypatch.setattr(workflows_service_module, "set_cache", AsyncMock())
monkeypatch.setattr(workflows_service_module, "invalidate_cache", AsyncMock())
workflows_dao = AsyncMock()
workflow = Workflow(id=uuid4(), slug="wf", folder_id=uuid4())
workflows_dao.fetch_artifact.return_value = workflow
workflows_dao.edit_artifact.return_value = workflow
return WorkflowsService(workflows_dao=workflows_dao), workflow.id


def _artifact_edit(service):
return service.workflows_dao.edit_artifact.await_args.kwargs["artifact_edit"]


def _request_body(workflow: dict):
"""The edit as the router gets it: parsed from the JSON body of PUT /workflows/{id}."""
return WorkflowEditRequest.model_validate({"workflow": workflow}).workflow


@pytest.mark.asyncio
async def test_a_null_folder_id_moves_the_workflow_to_the_top_level(service):
workflows_service, workflow_id = service

await workflows_service.edit_workflow(
project_id=uuid4(),
user_id=uuid4(),
workflow_edit=_request_body({"id": str(workflow_id), "folder_id": None}),
)

artifact_edit = _artifact_edit(workflows_service)
assert "folder_id" in artifact_edit.model_fields_set
assert artifact_edit.folder_id is None


@pytest.mark.asyncio
async def test_a_folder_id_in_the_body_moves_the_workflow_to_that_folder(service):
workflows_service, workflow_id = service
destination = uuid4()

await workflows_service.edit_workflow(
project_id=uuid4(),
user_id=uuid4(),
workflow_edit=_request_body(
{"id": str(workflow_id), "folder_id": str(destination)}
),
)

assert _artifact_edit(workflows_service).folder_id == destination


@pytest.mark.asyncio
async def test_an_edit_without_folder_id_keeps_the_folder(service):
workflows_service, workflow_id = service

await workflows_service.edit_workflow(
project_id=uuid4(),
user_id=uuid4(),
workflow_edit=_request_body({"id": str(workflow_id), "name": "Renamed"}),
)

assert "folder_id" not in _artifact_edit(workflows_service).model_fields_set


@pytest.mark.asyncio
async def test_an_application_rename_keeps_the_folder(service):
workflows_service, workflow_id = service

await ApplicationsService(workflows_service=workflows_service).edit_application(
project_id=uuid4(),
user_id=uuid4(),
application_edit=ApplicationEdit(id=workflow_id, name="Renamed"),
)

assert "folder_id" not in _artifact_edit(workflows_service).model_fields_set


@pytest.mark.asyncio
async def test_an_evaluator_rename_keeps_the_folder(service):
workflows_service, workflow_id = service

await EvaluatorsService(workflows_service=workflows_service).edit_evaluator(
project_id=uuid4(),
user_id=uuid4(),
evaluator_edit=EvaluatorEdit(id=workflow_id, name="Renamed"),
)

assert "folder_id" not in _artifact_edit(workflows_service).model_fields_set
2 changes: 1 addition & 1 deletion api/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "api"
version = "0.128.6"
version = "0.128.7"
description = "Agenta API"
requires-python = ">=3.11,<3.14"
authors = [
Expand Down
6 changes: 3 additions & 3 deletions api/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion clients/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "agenta-client"
version = "0.128.6"
version = "0.128.7"
description = "Fern-generated Python client for the Agenta API."
requires-python = ">=3.11,<3.14"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion clients/python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions hosting/kubernetes/helm/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ apiVersion: v2
name: agenta
description: A Helm chart for deploying Agenta (OSS or EE) on Kubernetes
type: application
version: 0.128.6
appVersion: "v0.128.6"
version: 0.128.7
appVersion: "v0.128.7"
keywords:
- agenta
- llm
Expand Down
2 changes: 1 addition & 1 deletion sdks/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "agenta"
version = "0.128.6"
version = "0.128.7"
description = "Agenta is the open-source workspace for your agents. Build agents through chat, improve them with feedback, and share them with your team."
readme = "README.md"
requires-python = ">=3.11,<3.14"
Expand Down
4 changes: 2 additions & 2 deletions sdks/python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading