Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
3faf5d5
chore(internal): reformat pyproject.toml
stainless-app[bot] May 4, 2026
cccb539
codegen metadata
stainless-app[bot] Apr 30, 2026
e90e040
codegen metadata
stainless-app[bot] Apr 30, 2026
5268da2
codegen metadata
stainless-app[bot] Apr 30, 2026
20e680f
codegen metadata
stainless-app[bot] May 1, 2026
332eb81
codegen metadata
stainless-app[bot] May 1, 2026
30a29a8
codegen metadata
stainless-app[bot] May 1, 2026
e7a3e52
codegen metadata
stainless-app[bot] May 1, 2026
62e5266
codegen metadata
stainless-app[bot] May 1, 2026
a7dda47
codegen metadata
stainless-app[bot] May 1, 2026
57074a1
codegen metadata
stainless-app[bot] May 1, 2026
e1f84c8
codegen metadata
stainless-app[bot] May 1, 2026
1f2e4a4
codegen metadata
stainless-app[bot] May 1, 2026
65f154d
codegen metadata
stainless-app[bot] May 2, 2026
2b244b3
codegen metadata
stainless-app[bot] May 2, 2026
b66cdbc
codegen metadata
stainless-app[bot] May 2, 2026
4743884
codegen metadata
stainless-app[bot] May 2, 2026
b5d691d
codegen metadata
stainless-app[bot] May 2, 2026
a2e308b
codegen metadata
stainless-app[bot] May 2, 2026
ae7d65a
codegen metadata
stainless-app[bot] May 2, 2026
11edd53
codegen metadata
stainless-app[bot] May 2, 2026
bb171e9
codegen metadata
stainless-app[bot] May 2, 2026
dd4f917
codegen metadata
stainless-app[bot] May 2, 2026
a7f90df
codegen metadata
stainless-app[bot] May 2, 2026
8bc1035
codegen metadata
stainless-app[bot] May 2, 2026
def72eb
codegen metadata
stainless-app[bot] May 2, 2026
3c5877f
codegen metadata
stainless-app[bot] May 3, 2026
eaea40d
codegen metadata
stainless-app[bot] May 3, 2026
6ac87a8
codegen metadata
stainless-app[bot] May 3, 2026
77fd2c7
codegen metadata
stainless-app[bot] May 3, 2026
c8392f2
codegen metadata
stainless-app[bot] May 3, 2026
7417bc5
codegen metadata
stainless-app[bot] May 3, 2026
1d151d2
codegen metadata
stainless-app[bot] May 3, 2026
05fd9e2
codegen metadata
stainless-app[bot] May 3, 2026
47f531d
codegen metadata
stainless-app[bot] May 3, 2026
5cf02fd
codegen metadata
stainless-app[bot] May 3, 2026
545842d
codegen metadata
stainless-app[bot] May 3, 2026
a4067ed
codegen metadata
stainless-app[bot] May 3, 2026
6f380ec
codegen metadata
stainless-app[bot] May 4, 2026
061c187
codegen metadata
stainless-app[bot] May 4, 2026
d4a182e
codegen metadata
stainless-app[bot] May 4, 2026
f7f758b
codegen metadata
stainless-app[bot] May 4, 2026
758089b
codegen metadata
stainless-app[bot] May 4, 2026
f441611
codegen metadata
stainless-app[bot] May 4, 2026
e76a2f5
codegen metadata
stainless-app[bot] May 4, 2026
29b0fc0
codegen metadata
stainless-app[bot] May 4, 2026
e614f67
codegen metadata
stainless-app[bot] May 4, 2026
d88aa94
codegen metadata
stainless-app[bot] May 4, 2026
1448a97
codegen metadata
stainless-app[bot] May 4, 2026
5715828
chore(internal): version bump
stainless-app[bot] May 4, 2026
b0569d0
codegen metadata
stainless-app[bot] May 4, 2026
f79a200
codegen metadata
stainless-app[bot] May 4, 2026
23fb7e4
codegen metadata
stainless-app[bot] May 4, 2026
8d89bfb
batch SGP span upserts (#331)
alvinkam2001 May 4, 2026
82871b3
codegen metadata
stainless-app[bot] May 4, 2026
63d4d50
codegen metadata
stainless-app[bot] May 5, 2026
8933473
codegen metadata
stainless-app[bot] May 5, 2026
3deed3a
codegen metadata
stainless-app[bot] May 5, 2026
cbdd298
codegen metadata
stainless-app[bot] May 5, 2026
e3fc47e
codegen metadata
stainless-app[bot] May 5, 2026
18d56e4
codegen metadata
stainless-app[bot] May 5, 2026
2f8a4db
codegen metadata
stainless-app[bot] May 5, 2026
f10e028
codegen metadata
stainless-app[bot] May 5, 2026
c938cbb
codegen metadata
stainless-app[bot] May 5, 2026
3ea4d35
codegen metadata
stainless-app[bot] May 5, 2026
dd01706
codegen metadata
stainless-app[bot] May 5, 2026
af02837
codegen metadata
stainless-app[bot] May 5, 2026
a6d478b
codegen metadata
stainless-app[bot] May 5, 2026
ba06702
chore(internal): reformat pyproject.toml
stainless-app[bot] May 4, 2026
168cc44
chore(internal): version bump
stainless-app[bot] May 4, 2026
ffaecd5
feat(api): api update
stainless-app[bot] May 5, 2026
4c5762b
release: 0.10.5
stainless-app[bot] May 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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.10.4"
".": "0.10.5"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 45
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sgp/agentex-sdk-c108a179582f0e0c6d479ea4b3bc6310a83693987073967c2b6203df23718eb2.yml
openapi_spec_hash: 53b8e5866709af71bef94816b8ede38b
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sgp/agentex-sdk-b997afde6595db62caea38bca035fda2812ea52cc8f360dab829b71178e826e6.yml
openapi_spec_hash: d195a98bf64b6edb826bc420773ca52e
config_hash: fb079ef7936611b032568661b8165f19
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## 0.10.5 (2026-05-05)

Full Changelog: [v0.10.4...v0.10.5](https://github.com/scaleapi/scale-agentex-python/compare/v0.10.4...v0.10.5)

### Features

* **api:** api update ([ffaecd5](https://github.com/scaleapi/scale-agentex-python/commit/ffaecd5a94b4082f9ef38d5c89286eabf3811759))
* **openai_agents:** expose real `usage`, `response_id`, plumb `previous_response_id`, opt-in `prompt_cache_key` for stateful responses and prompt caching ([#335](https://github.com/scaleapi/scale-agentex-python/issues/335)) ([ba5d64b](https://github.com/scaleapi/scale-agentex-python/commit/ba5d64be1f959ff1a35b30e647a0a5ead21a8402))


### Chores

* **internal:** reformat pyproject.toml ([ba06702](https://github.com/scaleapi/scale-agentex-python/commit/ba06702fd362656d594f73852ad2c690383892a8))
* **internal:** reformat pyproject.toml ([3faf5d5](https://github.com/scaleapi/scale-agentex-python/commit/3faf5d5927abdc3036862d4d06e085cda0eb6cd4))
* **internal:** version bump ([168cc44](https://github.com/scaleapi/scale-agentex-python/commit/168cc44f8199015e232cd2bddf1669a08ee90778))
* **internal:** version bump ([5715828](https://github.com/scaleapi/scale-agentex-python/commit/5715828a358c20b1cc895a696d0c8d803ec71932))

## 0.10.4 (2026-05-04)

Full Changelog: [v0.10.3...v0.10.4](https://github.com/scaleapi/scale-agentex-python/compare/v0.10.3...v0.10.4)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "agentex-sdk"
version = "0.10.4"
version = "0.10.5"
description = "The official Python library 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.10.4" # x-release-please-version
__version__ = "0.10.5" # x-release-please-version
78 changes: 48 additions & 30 deletions src/agentex/lib/core/tracing/processors/sgp_tracing_processor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from typing import override

import scale_gp_beta.lib.tracing as tracing
Expand Down Expand Up @@ -125,48 +127,64 @@ def _add_source_to_span(self, span: Span) -> None:

@override
async def on_span_start(self, span: Span) -> None:
self._add_source_to_span(span)
sgp_span = create_span(
name=span.name,
span_type=_get_span_type(span),
span_id=span.id,
parent_id=span.parent_id,
trace_id=span.trace_id,
input=span.input,
output=span.output,
metadata=span.data,
)
sgp_span.start_time = span.start_time.isoformat() # type: ignore[union-attr]
await self.on_spans_start([span])

@override
async def on_span_end(self, span: Span) -> None:
await self.on_spans_end([span])

@override
async def on_spans_start(self, spans: list[Span]) -> None:
if not spans:
return

sgp_spans: list[SGPSpan] = []
for span in spans:
self._add_source_to_span(span)
sgp_span = create_span(
name=span.name,
span_type=_get_span_type(span),
span_id=span.id,
parent_id=span.parent_id,
trace_id=span.trace_id,
input=span.input,
output=span.output,
metadata=span.data,
)
sgp_span.start_time = span.start_time.isoformat() # type: ignore[union-attr]
self._spans[span.id] = sgp_span
sgp_spans.append(sgp_span)

if self.disabled:
logger.warning("SGP is disabled, skipping span upsert")
return
# TODO(AGX1-198): Batch multiple spans into a single upsert_batch call
# instead of one span per HTTP request.
# https://linear.app/scale-epd/issue/AGX1-198/actually-use-sgp-batching-for-spans
await self.sgp_async_client.spans.upsert_batch( # type: ignore[union-attr]
items=[sgp_span.to_request_params()]
items=[s.to_request_params() for s in sgp_spans]
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.

self._spans[span.id] = sgp_span

@override
async def on_span_end(self, span: Span) -> None:
sgp_span = self._spans.pop(span.id, None)
if sgp_span is None:
logger.warning(f"Span {span.id} not found in stored spans, skipping span end")
async def on_spans_end(self, spans: list[Span]) -> None:
if not spans:
return

self._add_source_to_span(span)
sgp_span.input = span.input # type: ignore[assignment]
sgp_span.output = span.output # type: ignore[assignment]
sgp_span.metadata = span.data # type: ignore[assignment]
sgp_span.end_time = span.end_time.isoformat() # type: ignore[union-attr]

if self.disabled:
to_upsert: list[SGPSpan] = []
for span in spans:
sgp_span = self._spans.pop(span.id, None)
if sgp_span is None:
logger.warning(f"Span {span.id} not found in stored spans, skipping span end")
continue

self._add_source_to_span(span)
sgp_span.input = span.input # type: ignore[assignment]
sgp_span.output = span.output # type: ignore[assignment]
sgp_span.metadata = span.data # type: ignore[assignment]
sgp_span.end_time = span.end_time.isoformat() # type: ignore[union-attr]
to_upsert.append(sgp_span)

if self.disabled or not to_upsert:
return
await self.sgp_async_client.spans.upsert_batch( # type: ignore[union-attr]
items=[sgp_span.to_request_params()]
items=[s.to_request_params() for s in to_upsert]
)

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
from __future__ import annotations

import asyncio
from abc import ABC, abstractmethod

from agentex.types.span import Span
from agentex.lib.types.tracing import TracingProcessorConfig
from agentex.lib.utils.logging import make_logger

logger = make_logger(__name__)


class SyncTracingProcessor(ABC):
Expand Down Expand Up @@ -35,6 +41,43 @@ async def on_span_start(self, span: Span) -> None:
async def on_span_end(self, span: Span) -> None:
pass

async def on_spans_start(self, spans: list[Span]) -> None:
"""Batched variant of on_span_start.

Default fallback fans out to the single-span method in parallel so
existing processors keep working unchanged. Processors that support
real batching (e.g. sending all spans in one HTTP call) should
override this to avoid the per-span round trip.

Per-span exceptions are captured and logged individually so that one
failing span does not prevent the others from being processed.
"""
results = await asyncio.gather(
*(self.on_span_start(s) for s in spans), return_exceptions=True
)
for span, result in zip(spans, results):
if isinstance(result, Exception):
logger.error(
"Tracing processor %s failed on_span_start for span %s",
type(self).__name__,
span.id,
exc_info=result,
)
Comment thread
declan-scale marked this conversation as resolved.

async def on_spans_end(self, spans: list[Span]) -> None:
"""Batched variant of on_span_end. See on_spans_start for details."""
results = await asyncio.gather(
*(self.on_span_end(s) for s in spans), return_exceptions=True
)
for span, result in zip(spans, results):
if isinstance(result, Exception):
logger.error(
"Tracing processor %s failed on_span_end for span %s",
type(self).__name__,
span.id,
exc_info=result,
)

@abstractmethod
async def shutdown(self) -> None:
pass
43 changes: 27 additions & 16 deletions src/agentex/lib/core/tracing/span_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,29 +95,40 @@ async def _drain_loop(self) -> None:

@staticmethod
async def _process_items(items: list[_SpanQueueItem]) -> None:
"""Process a list of span events concurrently."""
"""Dispatch a batch of same-event-type items to each processor in one call.

async def _handle(item: _SpanQueueItem) -> None:
Groups spans by processor so each processor sees its full slice of the
drain batch at once. Processors that override the batched methods can
then send a single HTTP request per drain cycle instead of N.
"""
if not items:
return

event_type = items[0].event_type
assert all(i.event_type == event_type for i in items), (
"_process_items requires all items to share the same event_type; "
"callers must split START and END batches before dispatching."
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
by_processor: dict[AsyncTracingProcessor, list[Span]] = {}
for item in items:
for p in item.processors:
by_processor.setdefault(p, []).append(item.span)

async def _handle(p: AsyncTracingProcessor, spans: list[Span]) -> None:
try:
if item.event_type == SpanEventType.START:
coros = [p.on_span_start(item.span) for p in item.processors]
if event_type == SpanEventType.START:
await p.on_spans_start(spans)
else:
coros = [p.on_span_end(item.span) for p in item.processors]
results = await asyncio.gather(*coros, return_exceptions=True)
for result in results:
if isinstance(result, Exception):
logger.error(
"Tracing processor error during %s for span %s",
item.event_type.value,
item.span.id,
exc_info=result,
)
await p.on_spans_end(spans)
except Exception:
logger.exception(
"Unexpected error in span queue for span %s", item.span.id
"Tracing processor %s failed handling %d spans during %s",
type(p).__name__,
len(spans),
event_type.value,
)

await asyncio.gather(*[_handle(item) for item in items])
await asyncio.gather(*[_handle(p, spans) for p, spans in by_processor.items()])

# ------------------------------------------------------------------
# Shutdown
Expand Down
32 changes: 28 additions & 4 deletions src/agentex/resources/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,27 @@ def list(
order_direction: str | Omit = omit,
page_number: int | Omit = omit,
relationships: List[Literal["agents"]] | Omit = omit,
status: Optional[Literal["CANCELED", "COMPLETED", "FAILED", "RUNNING", "TERMINATED", "TIMED_OUT", "DELETED"]]
| Omit = omit,
task_metadata: Optional[str] | 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.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TaskListResponse:
"""
List all tasks.
"""List all tasks.

Args:
status: Filter tasks by status (e.g.

RUNNING, COMPLETED).

task_metadata:
JSON-encoded object used to filter tasks via JSONB containment. Example:
{"created_by_user_id": "abc-123"}.

extra_headers: Send extra headers

extra_query: Add additional query parameters to the request
Expand All @@ -144,6 +154,8 @@ def list(
"order_direction": order_direction,
"page_number": page_number,
"relationships": relationships,
"status": status,
"task_metadata": task_metadata,
},
task_list_params.TaskListParams,
),
Expand Down Expand Up @@ -679,17 +691,27 @@ async def list(
order_direction: str | Omit = omit,
page_number: int | Omit = omit,
relationships: List[Literal["agents"]] | Omit = omit,
status: Optional[Literal["CANCELED", "COMPLETED", "FAILED", "RUNNING", "TERMINATED", "TIMED_OUT", "DELETED"]]
| Omit = omit,
task_metadata: Optional[str] | 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.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> TaskListResponse:
"""
List all tasks.
"""List all tasks.

Args:
status: Filter tasks by status (e.g.

RUNNING, COMPLETED).

task_metadata:
JSON-encoded object used to filter tasks via JSONB containment. Example:
{"created_by_user_id": "abc-123"}.

extra_headers: Send extra headers

extra_query: Add additional query parameters to the request
Expand All @@ -714,6 +736,8 @@ async def list(
"order_direction": order_direction,
"page_number": page_number,
"relationships": relationships,
"status": status,
"task_metadata": task_metadata,
},
task_list_params.TaskListParams,
),
Expand Down
7 changes: 7 additions & 0 deletions src/agentex/types/agent_rpc_by_name_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ class ParamsCreateTaskRequest(TypedDict, total=False):
params: Optional[Dict[str, object]]
"""The parameters for the task"""

task_metadata: Optional[Dict[str, object]]
"""Caller-provided metadata to persist on the task row.

Only applied at task creation; ignored if a task with this name already exists.
Forwarded to the agent inside the ACP payload for backward compatibility.
"""


class ParamsCancelTaskRequest(TypedDict, total=False):
task_id: Optional[str]
Expand Down
7 changes: 7 additions & 0 deletions src/agentex/types/agent_rpc_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ class ParamsCreateTaskRequest(TypedDict, total=False):
params: Optional[Dict[str, object]]
"""The parameters for the task"""

task_metadata: Optional[Dict[str, object]]
"""Caller-provided metadata to persist on the task row.

Only applied at task creation; ignored if a task with this name already exists.
Forwarded to the agent inside the ACP payload for backward compatibility.
"""


class ParamsCancelTaskRequest(TypedDict, total=False):
task_id: Optional[str]
Expand Down
9 changes: 9 additions & 0 deletions src/agentex/types/task_list_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ class TaskListParams(TypedDict, total=False):
page_number: int

relationships: List[Literal["agents"]]

status: Optional[Literal["CANCELED", "COMPLETED", "FAILED", "RUNNING", "TERMINATED", "TIMED_OUT", "DELETED"]]
"""Filter tasks by status (e.g. RUNNING, COMPLETED)."""

task_metadata: Optional[str]
"""JSON-encoded object used to filter tasks via JSONB containment.

Example: {"created_by_user_id": "abc-123"}.
"""
Loading
Loading