Python: [Python] structured_outputs_transform reuses ChatHistory across calls (prompt pollution)
Maintainers usually reply within 2 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
Research direction
Read semantic_kernel/agents/orchestration/tools.py, focusing on structured_outputs_transform and its output_transform closure. Use the provided mocked repro to check the chat-history length across repeated calls; done means each invocation has only the system message and current message, producing lengths [2, 2, 2].
Written by the indexing model from the issue text.
Description
Description
structured_outputs_transform creates a single ChatHistory in the factory and closes over it. Each output_transform call add_messages into that same history, so prior orchestration outputs leak into later structured-output prompts and the history grows without bound (lengths 2→3→4…).
Environment
semantic-kernel==1.44.1(Python)- Python 3.13
- Repro uses a mocked chat service (no API keys)
Minimal repro
import asyncio
from typing import Any
from pydantic import BaseModel, PrivateAttr
from semantic_kernel.agents.orchestration.tools import structured_outputs_transform
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
from semantic_kernel.contents import ChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
class Out(BaseModel):
answer: str
class FakeSettings(PromptExecutionSettings):
response_format: Any = None
class FakeService(ChatCompletionClientBase):
_calls: list = PrivateAttr(default_factory=list)
def __init__(self) -> None:
super().__init__(service_id="fake", ai_model_id="fake")
def get_prompt_execution_settings_class(self):
return FakeSettings
async def get_chat_message_contents(self, chat_history, settings, **kwargs):
self._calls.append(len(chat_history.messages))
return [ChatMessageContent(role=AuthorRole.ASSISTANT, content='{"answer":"x"}')]
async def get_chat_message_content(self, chat_history, settings, **kwargs):
self._calls.append(len(chat_history.messages))
return ChatMessageContent(role=AuthorRole.ASSISTANT, content='{"answer":"x"}')
async def main():
svc = FakeService()
transform = structured_outputs_transform(Out, svc)
await transform(ChatMessageContent(role=AuthorRole.USER, content="first"))
await transform(ChatMessageContent(role=AuthorRole.USER, content="second"))
await transform(ChatMessageContent(role=AuthorRole.USER, content="third"))
print(svc._calls) # ACTUAL: [2, 3, 4] EXPECTED: [2, 2, 2]
asyncio.run(main())
Expected
Each transform invocation uses a fresh history (system + current message only), so independent orchestration steps do not pollute each other.
Actual
Shared history grows across calls; prior outputs leak into later structured-output prompts.
Root cause (pointer)
semantic_kernel/agents/orchestration/tools.py — structured_outputs_transform: chat_history = ChatHistory(...) created once in the factory and mutated inside output_transform.
Suggested fix direction
Construct a fresh ChatHistory (with the system message) inside output_transform on every call.
- Dominant language
- C#
- Stars
- 28.6k
- Forks
- 4.8k
- Avg merge
- 13h 24m
- Merged PRs (30d)
- 11
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/semantic-kernel
-
python triage
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
microsoft/semantic-kernel#14491 · 1 comment ·
Maintainers usually reply within 2 days
-
python triage
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
microsoft/semantic-kernel#14490 · 1 comment ·
Maintainers usually reply within 2 days
-
.NET python triage
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
microsoft/semantic-kernel#14482 · 3 comments ·
Maintainers usually reply within 2 days
-
python triage
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
microsoft/semantic-kernel#14481 ·
Maintainers usually reply within 2 days
-
python triage
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
microsoft/semantic-kernel#14460 ·
Maintainers usually reply within 2 days
All issues in microsoft/semantic-kernel
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15462 ·
Maintainers usually reply within 1 day
-
:watch: Not Triaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
comp:instrumentation.aspnetcore
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
open-telemetry/opentelemetry-dotnet-contrib#5427 ·
Maintainers usually reply within 1 day
-
design-proposal
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
dotnet/aspnetcore#69592 ·
Maintainers usually reply within 1 day
-
Client Container Registry customer-reported needs-team-attention question Service Attention
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Azure/azure-sdk-for-net#63470 · 3 comments · 1 reaction ·
Maintainers usually reply within 1 day