Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
64e7adc
docs(docs): add versioning concept page
mmabrouk Apr 17, 2026
efc96a9
docs(api): evaluators endpoint docstrings + concept page
mmabrouk Apr 17, 2026
315c81b
docs(api): folders endpoint docstrings + concept page
mmabrouk Apr 17, 2026
ba0125c
docs(api): applications endpoint docstrings + concept page
mmabrouk Apr 17, 2026
4b4cb37
docs(api): testsets endpoint docstrings + concept page
mmabrouk Apr 28, 2026
53e33e6
docs(api): workflows endpoint docstrings + concept page
mmabrouk Apr 28, 2026
111da5b
docs(api): tracing endpoint docstrings + concept page
mmabrouk Apr 28, 2026
08fd9da
Merge branch 'main' into docs/age-3734-versioning-guide
mmabrouk May 11, 2026
04d92c8
docs(docs): address review feedback on versioning page
mmabrouk May 11, 2026
9ce79d0
docs(api): umbrella for AGE-3734 API reference docs
mmabrouk May 11, 2026
dfd2e28
docs(docs): fix ag_config envelope in versioning example
mmabrouk May 11, 2026
a451ea3
Merge pull request #4175 from Agenta-AI/docs/age-3734-versioning-guide
mmabrouk May 11, 2026
f6817f7
Merge branch 'docs/age-3734-api-reference-epic' into docs/age-3734-ap…
mmabrouk May 11, 2026
fe90d03
Merge branch 'docs/age-3734-api-reference-epic' into docs/age-3734-fo…
mmabrouk May 11, 2026
5583d5f
docs(api): regenerate API docs after umbrella merge
mmabrouk May 11, 2026
6532e6a
docs(api): note 400 error path on application variant fork
mmabrouk May 11, 2026
910f54e
docs(api): revert auto-generated reference MDX
mmabrouk May 11, 2026
2120875
Merge pull request #4179 from Agenta-AI/docs/age-3734-folders-endpoints
mmabrouk May 11, 2026
99f67df
Merge remote-tracking branch 'origin/docs/age-3734-api-reference-epic…
mmabrouk May 11, 2026
130b3c7
docs(docs): address applications concept page review feedback
mmabrouk May 11, 2026
1375626
Merge pull request #4232 from Agenta-AI/docs/age-3734-applications-en…
mmabrouk May 11, 2026
d03d26d
Merge umbrella branch docs/age-3734-api-reference-epic into testsets
mmabrouk May 13, 2026
8ca7d91
Merge umbrella into evaluators
mmabrouk May 13, 2026
0ab2327
Merge umbrella into workflows
mmabrouk May 13, 2026
5e9bbb4
Merge umbrella into tracing
mmabrouk May 13, 2026
706e5f4
docs(docs): rewrite evaluators concept page per review
mmabrouk May 13, 2026
ddb0d1b
Merge pull request #4178 from Agenta-AI/docs/age-3734-evaluators-endp…
mmabrouk May 13, 2026
4a65541
Merge remote-tracking branch 'origin/docs/age-3734-api-reference-epic…
mmabrouk May 13, 2026
f4e373f
docs(docs): rewrite workflows concept page
mmabrouk May 13, 2026
5b92e18
docs(docs): restyle applications concept page to match the unified frame
mmabrouk May 13, 2026
00b6e7d
Merge pull request #4234 from Agenta-AI/docs/age-3734-workflows-endpo…
mmabrouk May 13, 2026
6d2b2e2
Merge remote-tracking branch 'origin/docs/age-3734-api-reference-epic…
mmabrouk May 13, 2026
bd8a11d
Merge remote-tracking branch 'origin/docs/age-3734-api-reference-epic…
mmabrouk May 13, 2026
370cfe9
docs(docs): restyle testsets concept page to match the unified frame
mmabrouk May 13, 2026
ab5ef71
docs(docs): restyle tracing concept page to match the unified frame
mmabrouk May 13, 2026
cc43cfb
docs(docs): address review feedback on testsets concept page
mmabrouk May 13, 2026
0fe972d
Merge pull request #4233 from Agenta-AI/docs/age-3734-testsets-endpoints
mmabrouk May 13, 2026
c0e9121
docs(docs): address review feedback on tracing concept page
mmabrouk May 13, 2026
0f6687a
docs(docs): tighten tracing page per Williams style audit
mmabrouk May 13, 2026
b23be25
Merge pull request #4235 from Agenta-AI/docs/age-3734-tracing-endpoints
mmabrouk May 13, 2026
12708e8
docs(docs): drop broken forward-links to evaluations concept page
mmabrouk May 13, 2026
9bac014
Merge branch 'release/v0.99.7' into docs/age-3734-api-reference-epic
jp-agenta May 14, 2026
1e7c9e8
docs(api,docs): address review feedback on umbrella PR #4314
mmabrouk May 14, 2026
987f2a1
docs(docs): reword __dedup_id__ section per review consensus
mmabrouk May 14, 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
661 changes: 575 additions & 86 deletions api/oss/src/apis/fastapi/applications/models.py

Large diffs are not rendered by default.

236 changes: 236 additions & 0 deletions api/oss/src/apis/fastapi/applications/router.py

Large diffs are not rendered by default.

557 changes: 450 additions & 107 deletions api/oss/src/apis/fastapi/evaluators/models.py

Large diffs are not rendered by default.

225 changes: 222 additions & 3 deletions api/oss/src/apis/fastapi/evaluators/router.py

Large diffs are not rendered by default.

47 changes: 37 additions & 10 deletions api/oss/src/apis/fastapi/folders/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional, List
from uuid import UUID

from pydantic import BaseModel
from pydantic import BaseModel, Field
from fastapi import HTTPException

from oss.src.utils.exceptions import Support
Expand All @@ -15,30 +15,57 @@


class FolderCreateRequest(BaseModel):
folder: FolderCreate
folder: FolderCreate = Field(
...,
description="Folder to create. `slug` is required; `parent_id` nests the new folder under an existing one.",
)


class FolderEditRequest(BaseModel):
folder: FolderEdit
folder: FolderEdit = Field(
...,
description="Folder edit payload. `id` must match the path parameter. Only fields present in the payload are changed.",
)


class FolderQueryRequest(BaseModel):
folder: FolderQuery
folder: FolderQuery = Field(
...,
description="Filter object. Any combination of `id`/`ids`, `slug`/`slugs`, `kind`/`kinds`, `parent_id`/`parent_ids`, `path`/`paths`, and `prefix`/`prefixes` narrows the result.",
)


class FolderResponse(Support):
count: int = 0
folder: Optional[Folder] = None
count: int = Field(
default=0,
description="Number of folders returned (`0` or `1`).",
)
folder: Optional[Folder] = Field(
default=None,
description="The folder, when found. Omitted when `count` is `0`.",
)


class FoldersResponse(Support):
count: int = 0
folders: List[Folder] = []
count: int = Field(
default=0,
description="Number of folders in `folders`.",
)
folders: List[Folder] = Field(
default_factory=list,
description="Matching folders for the query. Ordering is not guaranteed.",
)


class FolderIdResponse(Support):
count: int = 0
id: Optional[UUID] = None
count: int = Field(
default=0,
description="`1` if a folder was deleted, `0` if no folder matched.",
)
id: Optional[UUID] = Field(
default=None,
description="Id of the deleted folder. Omitted when nothing was deleted.",
)


class FolderNameInvalidException(HTTPException):
Expand Down
45 changes: 45 additions & 0 deletions api/oss/src/apis/fastapi/folders/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ async def create_folder(
#
folder_create_request: FolderCreateRequest,
) -> FolderResponse:
"""
Create a folder.

The folder name must match `[\\w -]+` (letters, digits, underscore,
space, hyphen); other characters return `400`. The resulting path
(the slug joined to the parent's path with a dot) must be unique
within the project, otherwise the call returns `409`. Passing a
`parent_id` that does not exist returns `404`. Paths are capped at
10 levels of nesting and slugs at 64 characters.
"""
if is_ee():
if not await check_action_access( # type: ignore
user_uid=request.state.user_id,
Expand Down Expand Up @@ -171,6 +181,12 @@ async def fetch_folder(
#
folder_id: UUID,
) -> FolderResponse:
"""
Fetch one folder by id.

Returns a single `folder` envelope. If the folder does not exist in
the caller's project, `count` is `0` and `folder` is omitted.
"""
if is_ee():
if not await check_action_access( # type: ignore
user_uid=request.state.user_id,
Expand Down Expand Up @@ -201,6 +217,15 @@ async def edit_folder(
folder_id: UUID,
folder_edit_request: FolderEditRequest,
) -> FolderResponse:
"""
Rename or move a folder.

Use this endpoint to change a folder's `slug`, `name`, or
`parent_id`. The `id` in the request body must match the path
parameter or the call returns `400`. Name and path-uniqueness rules
from create apply: invalid names return `400`, a path collision
returns `409`, and a missing `parent_id` returns `404`.
"""
if is_ee():
if not await check_action_access( # type: ignore
user_uid=request.state.user_id,
Expand Down Expand Up @@ -236,6 +261,15 @@ async def delete_folder(
#
folder_id: UUID,
) -> FolderIdResponse:
"""
Delete a folder and every descendant.

Removes the folder identified by `folder_id` together with every
folder beneath it, in a single transaction. Deletion is
unconditional; there is no archive or unarchive step. Resources
that were assigned to any of the removed folders continue to
exist and are no longer reachable through the deleted folder.
"""
if is_ee():
if not await check_action_access( # type: ignore
user_uid=request.state.user_id,
Expand Down Expand Up @@ -266,6 +300,17 @@ async def query_folders(
#
folder_query_request: FolderQueryRequest,
) -> FoldersResponse:
"""
Filter folders inside the caller's project.

Follows the general response envelope described in the
[Query Pattern](/reference/api-guide/query-pattern) guide, but
does not accept `windowing` or `include_archived` — folders are
hard-deleted and the response always returns the full filtered
set. Filters include `id`/`ids`, `slug`/`slugs`, `kind`/`kinds`,
`parent_id`/`parent_ids` (use `parent_id: null` for root folders),
`path`/`paths`, and `prefix`/`prefixes` for subtree lookup.
"""
if is_ee():
if not await check_action_access( # type: ignore
user_uid=request.state.user_id,
Expand Down
12 changes: 9 additions & 3 deletions api/oss/src/apis/fastapi/otlp/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from typing import List, Optional
from datetime import datetime

from pydantic import BaseModel, ConfigDict

from oss.src.utils.exceptions import Support
from pydantic import BaseModel, ConfigDict, Field

from oss.src.core.otel.dtos import (
OTelSpanDTO,
Expand All @@ -15,7 +14,14 @@


class CollectStatusResponse(Support):
status: str
"""OTLP endpoint readiness response."""

status: str = Field(
description=(
"Readiness string. `ready` means the router is mounted and "
"accepts OTLP ingest."
),
)


class OTelTracingResponse(Support):
Expand Down
35 changes: 35 additions & 0 deletions api/oss/src/apis/fastapi/otlp/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,48 @@ def __init__(

@intercept_exceptions()
async def otlp_status(self):
"""Return the OTLP endpoint liveness status.

Lightweight readiness probe. Returns `{"status": "ready"}` when
the router is mounted. Intended for health checks from OTel
collectors before they start exporting traces.
"""
return CollectStatusResponse(status="ready")

@intercept_exceptions()
async def otlp_ingest(
self,
request: Request,
):
"""Ingest traces via the OTLP/HTTP protobuf protocol.

This endpoint accepts a serialized
`ExportTraceServiceRequest` protobuf. Point any OTLP/HTTP
collector or SDK at `POST /otlp/v1/traces` and spans will flow
into the same ingest stream as the Agenta-native endpoints.

Use this when you already have OTel instrumentation emitting
OTLP. For new integrations that don't need raw OTLP, prefer
`POST /tracing/spans/ingest` — it takes JSON, accepts Agenta's
Comment thread
mmabrouk marked this conversation as resolved.
nested shape directly, and surfaces parse failures immediately.

## Content-Type and size limit

Binary protobuf only (`Content-Type: application/x-protobuf`).
JSON OTLP is not accepted. Requests larger than the configured
batch limit (default 4 MB, see `OTLP_MAX_BATCH_BYTES`) return
`413 Request Entity Too Large`.

## Response

Successful ingest returns `200 OK` with a serialized
`ExportTraceServiceResponse` protobuf. Parse failures on the
request body return `400`; malformed spans return `500`; quota
exhaustion returns `403`. Like the native ingest paths, spans
are queued on a Redis stream and persisted asynchronously — see
[Tracing — Async write
contract](/reference/api-guide/tracing#async-write-contract-202).
"""
# -------------------------------------------------------------------- #
# Permission check
# -------------------------------------------------------------------- #
Expand Down
57 changes: 45 additions & 12 deletions api/oss/src/apis/fastapi/testcases/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional, List
from uuid import UUID

from pydantic import BaseModel
from pydantic import BaseModel, Field

from oss.src.utils.exceptions import Support

Expand All @@ -18,23 +18,56 @@


class TestcasesQueryRequest(BaseModel):
testcase_ids: Optional[List[UUID]] = None
testcase_ids: Optional[List[UUID]] = Field(
default=None,
description="Explicit list of testcase IDs to fetch. Combine with `testset_id` or testset references to scope the lookup.",
)
#
testset_id: Optional[UUID] = None
testset_id: Optional[UUID] = Field(
default=None,
description="Return all testcases stored in this testset. The testset owns its testcases as a content-addressed bag; a revision references a subset of these.",
)
#
testset_ref: Optional[Reference] = None
testset_variant_ref: Optional[Reference] = None
testset_revision_ref: Optional[Reference] = None
testset_ref: Optional[Reference] = Field(
default=None,
description="Testset reference used to resolve the latest revision on the default variant. The revision's ordered testcase IDs are used for the lookup and pagination.",
)
testset_variant_ref: Optional[Reference] = Field(
default=None,
description="Testset variant reference used to resolve the latest revision on that variant.",
)
testset_revision_ref: Optional[Reference] = Field(
default=None,
description="Specific testset revision reference. The revision's ordered testcase IDs drive the lookup and cursor pagination.",
)
#
windowing: Optional[Windowing] = None
windowing: Optional[Windowing] = Field(
default=None,
description="Cursor-based pagination. When a revision reference is used, the cursor walks the revision's deterministic testcase ID list.",
)


class TestcaseResponse(Support):
count: int = 0
testcase: Optional[Testcase] = None
count: int = Field(
default=0,
description="1 if a testcase was returned, 0 otherwise.",
)
testcase: Optional[Testcase] = Field(
default=None,
description="The testcase blob. `data` carries the user-defined columns; `testcase_dedup_id` (inside `data`) is the caller-supplied dedup key when present.",
)


class TestcasesResponse(Support):
count: int = 0
testcases: List[Testcase] = []
windowing: Optional[Windowing] = None
count: int = Field(
default=0,
description="Number of testcases returned on this page.",
)
testcases: List[Testcase] = Field(
default_factory=list,
description="Testcase blobs matching the query, in revision-order when scoped by a revision reference.",
)
windowing: Optional[Windowing] = Field(
default=None,
description="Cursor for the next page, if more results exist.",
)
Loading
Loading