Skip to content
Open
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
lint:
timeout-minutes: 10
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/agentex-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -44,7 +44,7 @@ jobs:
permissions:
contents: read
id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/agentex-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
test:
timeout-minutes: 10
name: test
runs-on: ${{ github.repository == 'stainless-sdks/agentex-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down
4 changes: 2 additions & 2 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
".": "0.20.0",
"adk": "0.20.0"
".": "0.21.0",
"adk": "0.21.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 75
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sgp/agentex-sdk-b2df5f506330ad5fba5a0d518ab8a4bcf876e8c3684a4fe0d0cc5102fd9c569e.yml
openapi_spec_hash: 132e9efdb0535d9594abadf799431cf5
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sgp/agentex-sdk-132fed556e527565b396688a09e873b51ae42c6ad11757d49fc5ef1f2091c943.yml
openapi_spec_hash: 0c1d8518f9f60bb8dc325e26fe1ebadc
config_hash: 593e89b291976a5e84e4c3c3f8324354
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@

* **tracing:** emit OTel metrics for async span queue depth, batch drain, and SGP export success/failure (HTTP status labels). Disable SDK-side recording with ``AGENTEX_TRACING_METRICS=0``.

## 0.21.0 (2026-07-24)

Full Changelog: [agentex-client-v0.20.0...agentex-client-v0.21.0](https://github.com/scaleapi/scale-agentex-python/compare/agentex-client-v0.20.0...agentex-client-v0.21.0)

### Features

* **api:** add include_live parameter to schedules list method ([199fd6a](https://github.com/scaleapi/scale-agentex-python/commit/199fd6a633bc82626a4629ee1ac52a40e6f10271))
* **stlc:** configurable CI runner and private-production-repo support in workflow templates ([23c1b6b](https://github.com/scaleapi/scale-agentex-python/commit/23c1b6b8dc60f16c5e19a1aab68d244d286caffc))

## 0.20.0 (2026-07-16)

Full Changelog: [agentex-client-v0.19.0...agentex-client-v0.20.0](https://github.com/scaleapi/scale-agentex-python/compare/agentex-client-v0.19.0...agentex-client-v0.20.0)
Expand Down
8 changes: 8 additions & 0 deletions adk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 0.21.0 (2026-07-24)

Full Changelog: [agentex-sdk-v0.20.0...agentex-sdk-v0.21.0](https://github.com/scaleapi/scale-agentex-python/compare/agentex-sdk-v0.20.0...agentex-sdk-v0.21.0)

### Chores

* **agentex-sdk:** Synchronize agentex versions

## 0.20.0 (2026-07-16)

Full Changelog: [agentex-sdk-v0.19.0...agentex-sdk-v0.20.0](https://github.com/scaleapi/scale-agentex-python/compare/agentex-sdk-v0.19.0...agentex-sdk-v0.20.0)
Expand Down
2 changes: 1 addition & 1 deletion adk/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# (agentex/{__init__.py, _*.py, types/, resources/}) ships from the slim
# sibling package `agentex-client` which is pinned as a runtime dep.
name = "agentex-sdk"
version = "0.20.0"
version = "0.21.0"
description = "Agent Development Kit (ADK) overlay for the Agentex API — FastACP server, Temporal workflows, LLM provider integrations, observability"
license = "Apache-2.0"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# overlay (formerly `src/agentex/lib/*`) now lives in `adk/` and ships
# as the sibling `agentex-sdk` package — see `adk/pyproject.toml`.
name = "agentex-client"
version = "0.20.0"
version = "0.21.0"
description = "The official Python REST client for the Agentex API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion src/agentex/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "agentex"
__version__ = "0.20.0" # x-release-please-version
__version__ = "0.21.0" # x-release-please-version
83 changes: 83 additions & 0 deletions src/agentex/lib/core/tracing/obs_ids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Correlate adk business spans with the active observability trace.

The adk business ``trace_id`` is the agent **task id** (run-level: it spans the
whole agent run across many requests -- task/create, then each message/send turn),
so we must NOT overwrite it with a per-request observability trace_id. Doing so
would collapse the run-level grouping.

Instead, each business span is *tagged* with the active observability
trace_id/span_id (this is the OpenTelemetry "span link" pattern -- correlate
across trace granularities rather than merging them). You can then pivot from a
persisted business span to the Tempo/Datadog trace for the turn that produced it,
while the business trace still groups the entire run by task id.

Source selection follows SGP_OBS_MODE, matching egp-api-backend:
- unset / "dd_only": ddtrace context (current stack)
- "dual": OTel/LGTM preferred, ddtrace fallback
- "lgtm": OTel/LGTM only

This never fabricates ids -- if no observability context is active, it returns
an empty dict and the span is simply not tagged.
"""
from __future__ import annotations

import os
from typing import Dict, Optional, Tuple

__all__ = ("get_obs_mode", "obs_correlation")

DD_ONLY = "dd_only"
DUAL = "dual"
LGTM = "lgtm"
_DEFAULT_MODE = DD_ONLY
_VALID_MODES = (DD_ONLY, DUAL, LGTM)


def get_obs_mode() -> str:
"""Unset/empty/unrecognized -> ``dd_only`` (current behavior)."""
raw = os.getenv("SGP_OBS_MODE")
if not raw:
return _DEFAULT_MODE
mode = raw.strip().lower()
return mode if mode in _VALID_MODES else _DEFAULT_MODE


def _lgtm_ids() -> Optional[Tuple[str, str]]:
try:
from opentelemetry import trace
except ImportError:
return None
ctx = trace.get_current_span().get_span_context()
if ctx and ctx.is_valid:
return format(ctx.trace_id, "032x"), format(ctx.span_id, "016x")
return None


def _ddtrace_ids() -> Optional[Tuple[str, str]]:
try:
from ddtrace import tracer
except ImportError:
return None
ctx = tracer.current_trace_context()
if ctx and ctx.trace_id:
return format(ctx.trace_id, "032x"), format(ctx.span_id or 0, "016x")
return None


def obs_correlation() -> Dict[str, str]:
"""Return ``{"obs.trace_id": ..., "obs.span_id": ...}`` for the active
observability context, or ``{}`` if none is active.

Never fabricates ids -- this is a correlation tag, not the span's id.
"""
mode = get_obs_mode()
if mode == LGTM:
ids = _lgtm_ids()
elif mode == DUAL:
ids = _lgtm_ids() or _ddtrace_ids()
else: # dd_only
ids = _ddtrace_ids()

if not ids:
return {}
return {"obs.trace_id": ids[0], "obs.span_id": ids[1]}
13 changes: 13 additions & 0 deletions src/agentex/lib/core/tracing/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from agentex.types.span import Span
from agentex.lib.utils.logging import make_logger
from agentex.lib.utils.model_utils import recursive_model_dump
from agentex.lib.core.tracing.obs_ids import obs_correlation
from agentex.lib.core.tracing.span_error import set_span_error
from agentex.lib.core.tracing.span_queue import (
SpanEventType,
Expand Down Expand Up @@ -79,6 +80,12 @@ def start_span(

serialized_input = recursive_model_dump(input) if input else None
serialized_data = recursive_model_dump(data) if data else None
# Tag the business span with the active observability trace_id/span_id
# (OTel/ddtrace) so it can be correlated to the per-turn obs trace. The
# business trace_id stays the run-level task id -- see obs_ids.py.
obs = obs_correlation()
if obs:
serialized_data = {**(serialized_data or {}), **obs}
id = str(uuid.uuid4())

span = Span(
Expand Down Expand Up @@ -229,6 +236,12 @@ async def start_span(

serialized_input = recursive_model_dump(input) if input else None
serialized_data = recursive_model_dump(data) if data else None
# Tag the business span with the active observability trace_id/span_id
# (OTel/ddtrace) so it can be correlated to the per-turn obs trace. The
# business trace_id stays the run-level task id -- see obs_ids.py.
obs = obs_correlation()
if obs:
serialized_data = {**(serialized_data or {}), **obs}
id = str(uuid.uuid4())

span = Span(
Expand Down
22 changes: 20 additions & 2 deletions src/agentex/resources/agents/schedules.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ def list(
self,
agent_id: str,
*,
include_live: bool | Omit = omit,
limit: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
Expand All @@ -289,6 +290,8 @@ def list(
List run schedules for an agent.

Args:
include_live: Include live Temporal state and upcoming action times.

extra_headers: Send extra headers

extra_query: Add additional query parameters to the request
Expand All @@ -306,7 +309,13 @@ def list(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=maybe_transform({"limit": limit}, schedule_list_params.ScheduleListParams),
query=maybe_transform(
{
"include_live": include_live,
"limit": limit,
},
schedule_list_params.ScheduleListParams,
),
),
cast_to=ScheduleListResponse,
)
Expand Down Expand Up @@ -1056,6 +1065,7 @@ async def list(
self,
agent_id: str,
*,
include_live: bool | Omit = omit,
limit: int | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
Expand All @@ -1068,6 +1078,8 @@ async def list(
List run schedules for an agent.

Args:
include_live: Include live Temporal state and upcoming action times.

extra_headers: Send extra headers

extra_query: Add additional query parameters to the request
Expand All @@ -1085,7 +1097,13 @@ async def list(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=await async_maybe_transform({"limit": limit}, schedule_list_params.ScheduleListParams),
query=await async_maybe_transform(
{
"include_live": include_live,
"limit": limit,
},
schedule_list_params.ScheduleListParams,
),
),
cast_to=ScheduleListResponse,
)
Expand Down
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_create_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class ScheduleCreateResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
3 changes: 3 additions & 0 deletions src/agentex/types/agents/schedule_list_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@


class ScheduleListParams(TypedDict, total=False):
include_live: bool
"""Include live Temporal state and upcoming action times."""

limit: int
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_list_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class RunSchedule(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_pause_by_name_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class SchedulePauseByNameResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_pause_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class SchedulePauseResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_resume_by_name_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class ScheduleResumeByNameResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_resume_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class ScheduleResumeResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class ScheduleRetrieveByNameResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
6 changes: 6 additions & 0 deletions src/agentex/types/agents/schedule_retrieve_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ class ScheduleRetrieveResponse(BaseModel):
last_action_time: Optional[datetime] = None
"""When the schedule last fired."""

live_data_available: Optional[bool] = None
"""Whether requested live Temporal fields were retrieved successfully.

Null when live enrichment was not requested.
"""

next_action_times: Optional[List[datetime]] = None
"""Upcoming scheduled fire times."""

Expand Down
Loading
Loading