chore: release main - #525
Conversation
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ca6d0cf to
15c1480
Compare
Temporal workers registered tracing twice, and workflow replay emitted duplicate business logs. Keep tracing on the client and add replay-aware logging with workflow and run IDs. Changes: - Preserve inherited tracing and business-interceptor order. - Suppress replay logs without changing startup or activity logging. - Adopt the logger in SDK workflow sites and all Temporal templates. - Cover tracing composition, replay, and logging fields with tests. - Document adoption for existing agents. Linear: AGX1-1113
| RUN --mount=type=cache,target=/root/.cache/uv \ | ||
| --mount=type=secret,id=codeartifact-pip-conf,required=false \ | ||
| if [ -s /run/secrets/codeartifact-pip-conf ]; then \ | ||
| export UV_INDEX="scale-pypi=$(sed -n 's#.*index-url = https://aws:[^@]*@\(.*\)#https://\1#p' /run/secrets/codeartifact-pip-conf | head -1)"; \ | ||
| export UV_INDEX_SCALE_PYPI_USERNAME=aws; \ | ||
| export UV_INDEX_SCALE_PYPI_PASSWORD="$(sed -n 's#.*index-url = https://aws:\([^@]*\)@.*#\1#p' /run/secrets/codeartifact-pip-conf \ | ||
| | python3 -c 'import sys,urllib.parse;print(urllib.parse.unquote(sys.stdin.read().strip()))')"; \ | ||
| fi; \ | ||
| uv sync --no-dev |
There was a problem hiding this comment.
The second
uv sync runs after the project source is copied, while codeartifact-pip-conf and UV_INDEX_SCALE_PYPI_PASSWORD are still available. A project build backend, or a source dependency's build backend, can read and send that short-lived token elsewhere. Download authenticated artifacts while the secret is mounted, then remove the secret and credentials before any project-controlled build code runs. The same pattern appears in the other scaffold Dockerfiles.
How this was verified: The project build runs inside the same Docker step that mounts the token and exports its password.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/cli/templates/default/Dockerfile-uv.j2
Line: 53-61
Comment:
The second `uv sync` runs after the project source is copied, while `codeartifact-pip-conf` and `UV_INDEX_SCALE_PYPI_PASSWORD` are still available. A project build backend, or a source dependency's build backend, can read and send that short-lived token elsewhere. Download authenticated artifacts while the secret is mounted, then remove the secret and credentials before any project-controlled build code runs. The same pattern appears in the other scaffold Dockerfiles.
**How this was verified:** The project build runs inside the same Docker step that mounts the token and exports its password.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| async with inference_call(kwargs, args) as call: | ||
| # Return a single completion for non-streaming | ||
| response = call.observe(await llm.acompletion(*args, **kwargs)) |
There was a problem hiding this comment.
Only the async methods enter
inference_call; completion and completion_stream still call llm.completion directly. Native Anthropic, Bedrock, Vertex, and similar routes do not pass through the OpenAI client hook, so supported sync callers get no provider-aware GenAI metrics. Add a sync recorder lifecycle that covers success, errors, and full stream use; the null recorder also needs sync context support.
Knowledge Base Used: Observability
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/core/adapters/llm/adapter_litellm.py
Line: 43-45
Comment:
Only the async methods enter `inference_call`; `completion` and `completion_stream` still call `llm.completion` directly. Native Anthropic, Bedrock, Vertex, and similar routes do not pass through the OpenAI client hook, so supported sync callers get no provider-aware GenAI metrics. Add a sync recorder lifecycle that covers success, errors, and full stream use; the null recorder also needs sync context support.
**Knowledge Base Used:** [Observability](https://app.greptile.com/scale-ai/-/custom-context/knowledge-base/scaleapi/scale-agentex-python/-/docs/observability.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| ours = name == _PACKAGE_ROOT or name.startswith(_PACKAGE_ROOT + ".") | ||
| removed = 0 | ||
| for handler in list(existing.handlers): | ||
| if not ours and not getattr(handler, _OWNED_BY_MAKE_LOGGER, False): | ||
| continue | ||
| try: | ||
| handler.flush() # a buffering handler must not lose records on removal | ||
| except Exception: | ||
| pass | ||
| existing.removeHandler(handler) |
There was a problem hiding this comment.
ours is based on the logger name, so every handler on a propagating agentex.* logger is removed. This includes a file, audit, or filtered handler installed by the application; the ownership marker is checked only for other logger names. Use _OWNED_BY_MAKE_LOGGER for agentex.* handlers too, and leave unmarked handlers in place.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/utils/logging.py
Line: 204-213
Comment:
`ours` is based on the logger name, so every handler on a propagating `agentex.*` logger is removed. This includes a file, audit, or filtered handler installed by the application; the ownership marker is checked only for other logger names. Use `_OWNED_BY_MAKE_LOGGER` for `agentex.*` handlers too, and leave unmarked handlers in place.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| return genai.call( | ||
| provider=vendor, | ||
| operation=genai.CHAT, | ||
| model=model, |
There was a problem hiding this comment.
The repository rule says metric tags must not contain unbounded entity IDs. Here
model is an unrestricted caller string, and the code already notes that it may be a fine-tune ID or a dynamically built name. Passing it unchanged to genai.call can create a new Datadog series for every value. Use a bounded model family for metrics and keep the raw ID in traces or logs.
Rule Used: # Datadog High-Cardinality Tag Guard What: Prevent unbounded entity identifiers (user IDs, task IDs, request IDs, etc.) from being used as metric tag keys or values. Why: Each unique tag value creates a separate billable timeseries in Datad... (source)
Knowledge Base Used: Observability
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/core/adapters/llm/_genai_metrics.py
Line: 258
Comment:
The repository rule says metric tags must not contain unbounded entity IDs. Here `model` is an unrestricted caller string, and the code already notes that it may be a fine-tune ID or a dynamically built name. Passing it unchanged to `genai.call` can create a new Datadog series for every value. Use a bounded model family for metrics and keep the raw ID in traces or logs.
**Rule Used:** # Datadog High-Cardinality Tag Guard **What:** Prevent unbounded entity identifiers (user IDs, task IDs, request IDs, etc.) from being used as metric tag keys or values. **Why:** Each unique tag value creates a separate billable timeseries in Datad... ([source](https://app.greptile.com/scale-ai/-/custom-context?memory=3995666a-dc2a-4909-84bd-93d7ab0f3c6a))
**Knowledge Base Used:** [Observability](https://app.greptile.com/scale-ai/-/custom-context/knowledge-base/scaleapi/scale-agentex-python/-/docs/observability.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.15c1480 to
5f4ea04
Compare
| return genai.call( | ||
| provider=vendor, | ||
| operation=genai.CHAT, | ||
| model=model, |
There was a problem hiding this comment.
inference_call sends the caller's raw model string to genai.call. Fine-tune IDs and generated names can create a new metric series for every value. The repository rule says metric tags must not use unbounded entity IDs, including model IDs. Map this value to a small fixed model family or omit it.
Rule Used: # Datadog High-Cardinality Tag Guard What: Prevent unbounded entity identifiers (user IDs, task IDs, request IDs, etc.) from being used as metric tag keys or values. Why: Each unique tag value creates a separate billable timeseries in Datad... (source)
Knowledge Base Used: Observability
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/core/adapters/llm/_genai_metrics.py
Line: 255-258
Comment:
`inference_call` sends the caller's raw `model` string to `genai.call`. Fine-tune IDs and generated names can create a new metric series for every value. The repository rule says metric tags must not use unbounded entity IDs, including model IDs. Map this value to a small fixed model family or omit it.
**Rule Used:** # Datadog High-Cardinality Tag Guard **What:** Prevent unbounded entity identifiers (user IDs, task IDs, request IDs, etc.) from being used as metric tag keys or values. **Why:** Each unique tag value creates a separate billable timeseries in Datad... ([source](https://app.greptile.com/scale-ai/-/custom-context?memory=3995666a-dc2a-4909-84bd-93d7ab0f3c6a))
**Knowledge Base Used:** [Observability](https://app.greptile.com/scale-ai/-/custom-context/knowledge-base/scaleapi/scale-agentex-python/-/docs/observability.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| ours = name == _PACKAGE_ROOT or name.startswith(_PACKAGE_ROOT + ".") | ||
| removed = 0 | ||
| for handler in list(existing.handlers): | ||
| if not ours and not getattr(handler, _OWNED_BY_MAKE_LOGGER, False): | ||
| continue | ||
| try: | ||
| handler.flush() # a buffering handler must not lose records on removal | ||
| except Exception: | ||
| pass | ||
| existing.removeHandler(handler) |
There was a problem hiding this comment.
The
ours flag becomes true from the logger name alone, so this loop removes every handler on a propagating agentex.* logger. An app may add its own file or monitoring handler there; that handler has no SDK ownership mark but still gets removed. Remove only handlers marked by _OWNED_BY_MAKE_LOGGER.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/utils/logging.py
Line: 204-213
Comment:
The `ours` flag becomes true from the logger name alone, so this loop removes every handler on a propagating `agentex.*` logger. An app may add its own file or monitoring handler there; that handler has no SDK ownership mark but still gets removed. Remove only handlers marked by `_OWNED_BY_MAKE_LOGGER`.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Comments Outside DiffThese findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.
|
✨ Stainless prepared a new release
agentex-client: 0.28.0
0.28.0 (2026-09-18)
Full Changelog: agentex-client-v0.27.0...agentex-client-v0.28.0
Features
Bug Fixes
agentex-sdk: 0.28.0
0.28.0 (2026-09-18)
Full Changelog: agentex-sdk-v0.27.0...agentex-sdk-v0.28.0
Features
Bug Fixes
This pull request is managed by Stainless's GitHub App.
The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.
🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions
The PR is not safe to merge until raw model IDs stop reaching metric tags.
Fix with agent prompt
Summary
This release connects enabled
sgp-obstelemetry to Agentex servers, workers, model calls, logs, and shutdown. It also updates generated scaffolds for private package installs and keeps OpenAI Agents tracing available for the new observability bridge.sgp-obssetup for traces, metrics, and logs.0.28.0.Diagram
sequenceDiagram participant Caller participant ACP as FastACP participant Obs as sgp-obs participant Temporal participant Worker participant LLM as LiteLLMGateway participant Provider ACP->>Obs: "init_sgp_obs(app=self)" Caller->>ACP: HTTP request ACP->>Obs: bind(request_id) ACP->>Temporal: start or signal workflow Worker->>Obs: init_sgp_obs() Temporal->>Worker: workflow or activity Note over Worker: Replay logs are skipped Worker->>LLM: model call alt Async LiteLLM call LLM->>Obs: inference_call(model) LLM->>Provider: acompletion Provider-->>LLM: response or stream LLM->>Obs: observe response else Sync LiteLLM call LLM->>Provider: completion Provider-->>LLM: response or stream Note over LLM,Obs: No inference_call runs end LLM-->>Worker: completion Worker-->>Temporal: activity result Temporal-->>ACP: accepted work ACP->>Obs: reset(request_id) ACP-->>Caller: response Note over ACP,Worker: On shutdown, drain async spans, sync processors, then sgp-obsReviews (1) · Last reviewed commit: "chore: release main"