Skip to content

[Bug] BYOK Anthropic provider missing multiple session events (turn_start, turn_end, reasoning, usage_info) #1064

@lynn-orrell

Description

@lynn-orrell

Description

When using a BYOK Anthropic provider (type: "anthropic"), the assistant.turn_start and assistant.turn_end events are never emitted. All other events (assistant.usage, assistant.message, tool.execution_start, tool.execution_complete, session.idle) fire correctly.

The same session setup with the default backend or a BYOK OpenAI provider emits all turn lifecycle events as expected.

Reproduction

import asyncio
import os
from copilot import CopilotClient
from copilot.client import SubprocessConfig
from copilot.session import (
    InfiniteSessionConfig,
    PermissionHandler,
    SystemMessageReplaceConfig,
)
from copilot.tools import Tool, ToolResult

async def my_tool(args):
    return ToolResult(content="42")

async def run_test(client, label, model, provider=None):
    events = []
    def on_event(event):
        events.append(event.type.value)

    kwargs = dict(
        on_permission_request=PermissionHandler.approve_all,
        model=model,
        system_message=SystemMessageReplaceConfig(
            mode="replace", content="Use my_tool when asked."
        ),
        available_tools=["my_tool"],
        streaming=True,
        tools=[Tool(name="my_tool", description="Returns 42", handler=my_tool)],
        infinite_sessions=InfiniteSessionConfig(enabled=False),
        on_event=on_event,
    )
    if provider:
        kwargs["provider"] = provider

    session = await client.create_session(**kwargs)
    try:
        await session.send_and_wait("Use my_tool to get the answer.", timeout=60)
    finally:
        await session.disconnect()

    unique = set(events)
    ts = events.count("assistant.turn_start")
    te = events.count("assistant.turn_end")
    print(f"{label}: turn_start={ts}, turn_end={te}, "
          f"usage={events.count('assistant.usage')}, "
          f"message={events.count('assistant.message')}")
    return unique

async def main():
    endpoint = os.environ["AZURE_FOUNDRY_ENDPOINT"].rstrip("/")
    api_key = os.environ["AZURE_FOUNDRY_API_KEY"]
    client = CopilotClient(SubprocessConfig())

    try:
        # 1. Default backend -- turn events fire
        default = await run_test(client, "Default (claude-haiku-4.5)", "claude-haiku-4.5")

        # 2. BYOK Anthropic (Haiku) -- turn events MISSING
        byok_haiku = await run_test(client, "BYOK Anthropic (claude-haiku-4-5)", "claude-haiku-4-5", {
            "type": "anthropic",
            "base_url": f"{endpoint}/anthropic",
            "api_key": api_key,
        })

        # 3. BYOK Anthropic (Sonnet) -- turn events MISSING
        byok_sonnet = await run_test(client, "BYOK Anthropic (claude-sonnet-4-6)", "claude-sonnet-4-6", {
            "type": "anthropic",
            "base_url": f"{endpoint}/anthropic",
            "api_key": api_key,
        })

        # 4. BYOK OpenAI -- turn events fire
        byok_openai = await run_test(client, "BYOK OpenAI (gpt-5.4-nano)", "gpt-5.4-nano", {
            "type": "openai",
            "base_url": f"{endpoint}/openai/v1/",
            "api_key": api_key,
            "wire_api": "responses",
        })

        # Show events emitted by both Default and BYOK OpenAI
        # but never delivered to on_event by BYOK Anthropic
        missing = sorted((default | byok_openai) - byok_haiku)
        if missing:
            print(f"\nEvents missing from BYOK Anthropic that both others emit:")
            for e in missing:
                print(f"  - {e}")
    finally:
        await client.stop()

asyncio.run(main())

Requires AZURE_FOUNDRY_ENDPOINT and AZURE_FOUNDRY_API_KEY env vars pointing to an Azure AI Foundry deployment with Anthropic and OpenAI models available.

Actual output (SDK 0.2.2)

Default (claude-haiku-4.5):              turn_start=2, turn_end=2, usage=2, message=2
BYOK Anthropic (claude-haiku-4-5):       turn_start=0, turn_end=0, usage=2, message=2
BYOK Anthropic (claude-sonnet-4-6):      turn_start=0, turn_end=0, usage=2, message=2
BYOK OpenAI (gpt-5.4-nano):             turn_start=2, turn_end=2, usage=2, message=2

Events missing from BYOK Anthropic that both others emit:
  - assistant.reasoning
  - assistant.reasoning_delta
  - assistant.turn_end
  - assistant.turn_start
  - session.custom_agents_updated
  - session.usage_info

Expected output

All three backends should emit assistant.turn_start and assistant.turn_end. The streaming events documentation lists both as non-ephemeral events that are always emitted, and the agent loop documentation states "the number of assistant.turn_start / assistant.turn_end pairs equals the total number of LLM API calls."

Additional missing events with BYOK Anthropic

Comparing unique event types across all three backends, BYOK Anthropic also does not emit these events that both Default and BYOK OpenAI do emit:

  • assistant.reasoning
  • assistant.reasoning_delta
  • session.usage_info

Note: session.custom_agents_updated is missing from both BYOK providers (Anthropic and OpenAI) but present in the default backend -- this may be a separate, broader BYOK issue.

Impact

Code that tracks per-turn token usage by accumulating assistant.usage data between turn_start/turn_end boundaries gets no per-turn stats with BYOK Anthropic. The assistant.usage events themselves fire correctly, so session-level totals can still be computed, but turn-level granularity is lost.

Environment

  • SDK: github-copilot-sdk==0.2.2 (also confirmed on 0.2.1)
  • Provider: Azure AI Foundry with type: "anthropic" provider config
  • Models tested: claude-haiku-4-5 and claude-sonnet-4-6 via BYOK Anthropic -- both missing turn events
  • Controls: Default backend (claude-haiku-4.5) and BYOK OpenAI (gpt-5.4-nano) on the same Foundry endpoint both emit turn events correctly
  • OS: Windows 11
  • Python: 3.12

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions