Skip to content

feat: Converse API Adapter: enable request metadata traces - #473

Open
AlexFoxalt wants to merge 8 commits into
developmentfrom
feat/enable-request-metadata-traces
Open

feat: Converse API Adapter: enable request metadata traces#473
AlexFoxalt wants to merge 8 commits into
developmentfrom
feat/enable-request-metadata-traces

Conversation

@AlexFoxalt

@AlexFoxalt AlexFoxalt commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Support Bedrock request metadata tagging for Converse API-backed deployments:

  • Model invocation logging

  • Per-request metadata tagging

  • Added configurable Converse requestMetadata propagation via CONVERSE_API_REQUEST_METADATA_FIELDS, extracting selected DIAL UserInfo paths such as roles.0, project, and userClaims.email.

  • Added Bedrock-compatible request metadata handling, including entry count, key/value length, and character constraints.

  • Added boto as a supported Claude client mode to route Claude chat completions through the Converse API path.

  • Updated docs, env examples, and tests for request metadata tracing and Claude client selection.

  • Bumped related DIAL/Anthropic dependencies and adjusted ORT config.

@AlexFoxalt
AlexFoxalt requested a review from adubovik as a code owner July 30, 2026 10:25
Comment thread README.md Outdated
Comment thread aidial_adapter_bedrock/llm/converse/adapter.py Outdated
Comment thread aidial_adapter_bedrock/llm/converse/configuration.py
Comment thread README.md Outdated
Comment thread aidial_adapter_bedrock/llm/converse/types.py Outdated
from typing import Literal

from aidial_sdk.chat_completion import Request as ChatCompletionRequest
from aidial_sdk.chat_completion import Request

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this change

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted

def has_converse_api_configuration(
request: Request | None, upstream_config: UpstreamConfig
) -> bool:
cond1 = None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Why is boolean condition is assigned None? Is there a difference between False and None?
  2. Why did use overcomplicated any([cond1,cond2]) instead of just cond1 or cond2?

Keep it simple:

    configuration = (
        cf.configuration
        if (request and (cf := request.custom_fields))
        else None
    )
    if configuration and (
        "performanceConfig" in configuration
        or "guardrailConfig" in configuration
    ):
        return True

    return (
        isinstance(upstream_config, CloudUpstreamConfig)
        and upstream_config.claude_client == "converse"
    )

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced with suggested code

from aidial_adapter_bedrock.utils.log_config import bedrock_logger as log

CONVERSE_API_REQUEST_METADATA_FIELDS = os.getenv(
"CONVERSE_API_REQUEST_METADATA_FIELDS"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the env helper:

def get_env_list(name: str) -> list[str] | None:
    if (value := os.getenv(name)) is not None:
        return [str.strip(s) for s in value.split(",")]
    return None



def resolve_paths(
data: dict[str, Any], config: str | None = None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really "config", but "paths"

Comment thread README.md
|AWS_SESSION_TOKEN|NA|AWS session token with an access the Bedrock service|
|AWS_DEFAULT_REGION||AWS region e.g. `us-east-1`|
|AWS_CLAUDE_DEFAULT_CLIENT|legacy|Default AWS Claude client mode for cloud credentials path. Supported values: `legacy`, `mantle`.|
|AWS_CLAUDE_DEFAULT_CLIENT|legacy|Default AWS Claude client mode for cloud credentials path. Supported values: `legacy`, `mantle`, `boto`.|

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

converse, not boto

Comment thread README.md
|`userClaims`|User claims, addressable by nested paths, e.g. `userClaims.email`|

Paths use dot-separated object keys and integer list indices. Unset, empty, or
whitespace-only configuration disables the feature. Unresolvable paths are

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unset, empty, or whitespace-only configuration disables the feature

This is redundant and obvious.

Comment thread README.md
ignored. The `*` wildcard is not supported.

Bedrock `requestMetadata` limits apply: at most 16 entries, keys and values up
to 256 characters, and only characters matching `[a-zA-Z0-9\s:_@$#=/+,-.]`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[a-zA-Z0-9\s:_@$#=/+,-.]

Where is this set of symbols documented?

_MAX_ENTRIES = 16
_MAX_KEY_LEN = 256
_MAX_VALUE_LEN = 256
_ALLOWED = re.compile(r"[^a-zA-Z0-9\s:_@$#=/+,.\-]")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not _ALLOWED, but _DISALLOWED

)
break

safe_key = _ALLOWED.sub("", key)[:_MAX_KEY_LEN]

@adubovik adubovik Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's replace with a valid placeholder symbol, e.g. "_", to preserve the original length.


def from_user_info(user_info: UserInfo) -> dict[str, str]:
data = user_info.model_dump(mode="json")
return _to_bedrock_metadata(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's print the data in a debug log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants