mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 11:19:56 +00:00
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> OpenAI recently starts providing token usage information on their streaming chat completion API. This ADR opens the discussion on how we should consume that information within our `StreamingChatMessageContent` data structure. ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> 1. An ADR documenting 4 approaches. 2. Implementation of the selected approach. 3. Fix issues where data in the streaming contents are modified after concatenation (`__add__`). 4. Add streaming to model diagnostics operation names so that we can distinguish streaming and non-streaming operations. ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄 --------- Co-authored-by: Ben Thomas <[email protected]>
56 lines
2.7 KiB
Python
56 lines
2.7 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
from semantic_kernel.contents.streaming_content_mixin import StreamingContentMixin
|
|
from semantic_kernel.contents.text_content import TextContent
|
|
from semantic_kernel.exceptions import ContentAdditionException
|
|
|
|
|
|
class StreamingTextContent(StreamingContentMixin, TextContent):
|
|
"""This represents streaming text response content.
|
|
|
|
Args:
|
|
choice_index: int - The index of the choice that generated this response.
|
|
inner_content: Optional[Any] - The inner content of the response,
|
|
this should hold all the information from the response so even
|
|
when not creating a subclass a developer can leverage the full thing.
|
|
ai_model_id: Optional[str] - The id of the AI model that generated this response.
|
|
metadata: Dict[str, Any] - Any metadata that should be attached to the response.
|
|
text: Optional[str] - The text of the response.
|
|
encoding: Optional[str] - The encoding of the text.
|
|
|
|
Methods:
|
|
__str__: Returns the text of the response.
|
|
__bytes__: Returns the content of the response encoded in the encoding.
|
|
__add__: Combines two StreamingTextContent instances.
|
|
"""
|
|
|
|
def __bytes__(self) -> bytes:
|
|
"""Return the content of the response encoded in the encoding."""
|
|
return self.text.encode(self.encoding if self.encoding else "utf-8") if self.text else b""
|
|
|
|
def __add__(self, other: TextContent) -> "StreamingTextContent":
|
|
"""When combining two StreamingTextContent instances, the text fields are combined.
|
|
|
|
The addition should follow these rules:
|
|
1. The inner_content of the two will be combined. If they are not lists, they will be converted to lists.
|
|
2. ai_model_id should be the same.
|
|
3. encoding should be the same.
|
|
4. choice_index should be the same.
|
|
5. Metadata will be combined.
|
|
"""
|
|
if isinstance(other, StreamingTextContent) and self.choice_index != other.choice_index:
|
|
raise ContentAdditionException("Cannot add StreamingTextContent with different choice_index")
|
|
if self.ai_model_id != other.ai_model_id:
|
|
raise ContentAdditionException("Cannot add StreamingTextContent from different ai_model_id")
|
|
if self.encoding != other.encoding:
|
|
raise ContentAdditionException("Cannot add StreamingTextContent with different encoding")
|
|
|
|
return StreamingTextContent(
|
|
choice_index=self.choice_index,
|
|
inner_content=self._merge_inner_contents(other.inner_content),
|
|
ai_model_id=self.ai_model_id,
|
|
metadata=self.metadata,
|
|
text=(self.text or "") + (other.text or ""),
|
|
encoding=self.encoding,
|
|
)
|