Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 20 additions & 1 deletion openapi/ga/individual/platform.openapi.yaml

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

21 changes: 20 additions & 1 deletion openapi/ga/openapi.yaml

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

21 changes: 20 additions & 1 deletion openapi/openapi.yaml

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

21 changes: 20 additions & 1 deletion sdk/python/nemo-platform/.nmpcontext/openapi.yaml

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

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

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

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

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

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

10 changes: 10 additions & 0 deletions services/intake/src/nmp/intake/api/v2/experiments/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from nmp.common.entities.client import EntityClient, EntityConflictError, EntityNotFoundError
from nmp.common.service.dependencies import get_entity_client
from nmp.intake.api.v2.experiments.schemas import (
EXPERIMENT_SESSION_SUMMARY_INPUT_CHAR_LIMIT,
EvaluatorAggregate,
ExperimentFilter,
ExperimentGroupFilter,
Expand All @@ -33,6 +34,7 @@
ExperimentRequest,
ExperimentResponse,
ExperimentSessionFilter,
ExperimentSessionMode,
ExperimentSessionResponse,
)
from nmp.intake.entities.experiments import Experiment, ExperimentGroup, SortCriterion
Expand Down Expand Up @@ -654,6 +656,13 @@ async def list_experiment_sessions(
parsed: ExperimentSessionFilterDep,
page: int = Query(default=1, ge=1, description="Page number."),
page_size: int = Query(default=100, ge=1, le=1000, description="Page size."),
mode: ExperimentSessionMode = Query(
default="detailed",
description=(
"Response payload mode. summary keeps the same session row fields but truncates root-span input "
"to 1000 characters; detailed returns the full root-span input."
),
),
) -> Page[ExperimentSessionResponse]:
validate_list_query_params(request)
experiment = await _get_or_404(
Expand Down Expand Up @@ -686,6 +695,7 @@ async def list_experiment_sessions(
test_case_id=test_case_id,
page=page,
page_size=page_size,
input_char_limit=EXPERIMENT_SESSION_SUMMARY_INPUT_CHAR_LIMIT if mode == "summary" else None,
)
except Exception as exc:
# Sessions are the response payload (not enrichment), so we can't silently degrade like
Expand Down
13 changes: 11 additions & 2 deletions services/intake/src/nmp/intake/api/v2/experiments/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
from __future__ import annotations

from datetime import datetime
from typing import Annotated, Any
from typing import Annotated, Any, Literal

from nmp.common.entities.values import DatetimeFilter, Filter, NumberFilter, map_entity_field
from nmp.intake.entities.experiments import Experiment, ExperimentGroup, SortCriterion
from nmp.intake.spans.domain import SpanStatus
from nmp.intake.spans.experiment_session_repository import ExperimentSessionRow
from pydantic import AnyUrl, BaseModel, ConfigDict, Field

ExperimentSessionMode = Literal["summary", "detailed"]
EXPERIMENT_SESSION_SUMMARY_INPUT_CHAR_LIMIT = 1000


class ExperimentGroupRequest(BaseModel):
"""Request body for creating an ExperimentGroup."""
Expand Down Expand Up @@ -292,7 +295,13 @@ class ExperimentSessionResponse(BaseModel):
latency_ms: float | None = None

status: SpanStatus = Field(description="Root-span status: success, error, cancelled, or unknown.")
input: str | None = Field(default=None, description="Root-span input text (the query).")
input: str | None = Field(
default=None,
description=(
"Root-span input text. In summary mode this is truncated to "
f"{EXPERIMENT_SESSION_SUMMARY_INPUT_CHAR_LIMIT} characters."
),
)

input_tokens: int | None = Field(default=None, description="Sum of input tokens across this session's spans.")
output_tokens: int | None = Field(default=None, description="Sum of output tokens across this session's spans.")
Expand Down
17 changes: 15 additions & 2 deletions services/intake/src/nmp/intake/spans/api/spans_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class SpanGroupBy(StrEnum):


SpanMode = Literal["summary", "detailed"]
SPAN_SUMMARY_ERROR_MESSAGE_CHAR_LIMIT = 1000


class SpanFilter(BaseModel):
Expand Down Expand Up @@ -130,7 +131,13 @@ class Span(BaseModel):
ended_at: datetime | None = None
status: SpanStatus
error_type: str | None = None
error_message: str | None = None
error_message: str | None = Field(
default=None,
description=(
"Normalized error message. In summary mode this is truncated to "
f"{SPAN_SUMMARY_ERROR_MESSAGE_CHAR_LIMIT} characters."
),
)
provider: str | None = None
model: str | None = None
prompt_id: str | None = None
Expand Down Expand Up @@ -176,7 +183,7 @@ def from_domain(cls, span: IntakeSpan, *, mode: SpanMode = "detailed") -> Self:
ended_at=span.end_time,
status=span.status,
error_type=semantic_attributes.error_type,
error_message=None if summary else semantic_attributes.error_message,
error_message=_error_message_for_mode(semantic_attributes.error_message, summary=summary),
provider=semantic_attributes.provider,
model=semantic_attributes.model,
prompt_id=semantic_attributes.prompt_id,
Expand Down Expand Up @@ -241,3 +248,9 @@ def _float_or_none(value: object) -> float | None:
if isinstance(value, str | int | float | Decimal):
return float(value)
raise TypeError(f"Expected float-compatible span value, got {type(value).__name__}")


def _error_message_for_mode(value: str | None, *, summary: bool) -> str | None:
if value is None or not summary:
return value
return value[:SPAN_SUMMARY_ERROR_MESSAGE_CHAR_LIMIT]
Loading