Skip to content

Commit

Permalink
[Python] Conversation summary skill porting part I (microsoft#853)
Browse files Browse the repository at this point in the history
### Motivation and Context
Porting conversation summary skill. Integration test is added as well.
Action item and topic will be ported later.

---------

Co-authored-by: Po-Wei Huang <[email protected]>
Co-authored-by: Abby Harrison <[email protected]>
Co-authored-by: Abby Harrison <[email protected]>
Co-authored-by: Devis Lucato <[email protected]>
Co-authored-by: Devis Lucato <[email protected]>
  • Loading branch information
6 people authored May 13, 2023
1 parent 5731030 commit 9fccbe4
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 1 deletion.
2 changes: 1 addition & 1 deletion FEATURE_MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
| | C# | Python | Notes |
|---|---|---|---|
| TextMemorySkill ||| |
| ConversationSummarySkill || | |
| ConversationSummarySkill || | |
| FileIOSkill ||| |
| HttpSkill ||| |
| MathSkill ||| |
Expand Down
4 changes: 4 additions & 0 deletions python/semantic_kernel/core_skills/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Copyright (c) Microsoft. All rights reserved.

from semantic_kernel.core_skills.conversation_summary_skill import (
ConversationSummarySkill,
)
from semantic_kernel.core_skills.file_io_skill import FileIOSkill
from semantic_kernel.core_skills.http_skill import HttpSkill
from semantic_kernel.core_skills.math_skill import MathSkill
Expand All @@ -14,5 +17,6 @@
"TimeSkill",
"HttpSkill",
"BasicPlanner",
"ConversationSummarySkill",
"MathSkill",
]
63 changes: 63 additions & 0 deletions python/semantic_kernel/core_skills/conversation_summary_skill.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright (c) Microsoft. All rights reserved.

from semantic_kernel.kernel import Kernel
from semantic_kernel.orchestration.sk_context import SKContext
from semantic_kernel.skill_definition import sk_function
from semantic_kernel.text import text_chunker
from semantic_kernel.text.function_extension import aggregate_chunked_results_async


class ConversationSummarySkill:
"""
Semantic skill that enables conversations summarization.
"""

# The max tokens to process in a single semantic function call.
_max_tokens = 1024

_summarize_conversation_prompt_template = (
"BEGIN CONTENT TO SUMMARIZE:\n"
"{{" + "$INPUT" + "}}\n"
"END CONTENT TO SUMMARIZE.\n"
"Summarize the conversation in 'CONTENT TO SUMMARIZE',\
identifying main points of discussion and any conclusions that were reached.\n"
"Do not incorporate other general knowledge.\n"
"Summary is in plain text, in complete sentences, with no markup or tags.\n"
"\nBEGIN SUMMARY:\n"
)

def __init__(self, kernel: Kernel):
self._summarizeConversationFunction = kernel.create_semantic_function(
ConversationSummarySkill._summarize_conversation_prompt_template,
skill_name=ConversationSummarySkill.__name__,
description="Given a section of a conversation transcript, summarize the part of the conversation.",
max_tokens=ConversationSummarySkill._max_tokens,
temperature=0.1,
top_p=0.5,
)

@sk_function(
description="Given a long conversation transcript, summarize the conversation.",
name="SummarizeConversation",
input_description="A long conversation transcript.",
)
async def summarize_conversation_async(
self, input: str, context: SKContext
) -> SKContext:
"""
Given a long conversation transcript, summarize the conversation.
:param input: A long conversation transcript.
:param context: The SKContext for function execution.
:return: SKContext with the summarized conversation result.
"""
lines = text_chunker._split_text_lines(
input, ConversationSummarySkill._max_tokens, True
)
paragraphs = text_chunker._split_text_paragraph(
lines, ConversationSummarySkill._max_tokens
)

return await aggregate_chunked_results_async(
self._summarizeConversationFunction, paragraphs, context
)
57 changes: 57 additions & 0 deletions python/tests/integration/completions/e2e_text_completion.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
# Copyright (c) Microsoft. All rights reserved.

import logging
import time

import semantic_kernel as sk
from semantic_kernel.core_skills.conversation_summary_skill import (
ConversationSummarySkill,
)

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger()
Expand Down Expand Up @@ -316,3 +321,55 @@ async def simple_completion(kernel: sk.Kernel):
f"Completion using context, additional variables, and additional string: '{output}'"
)
assert len(output) > 0


async def summarize_conversation_using_skill(kernel: sk.Kernel):
ChatTranscript = """John: Hello, how are you?
Jane: I'm fine, thanks. How are you?
John: I'm doing well, writing some example code.
Jane: That's great! I'm writing some example code too.
John: What are you writing?
Jane: I'm writing a chatbot.
John: That's cool. I'm writing a chatbot too.
Jane: What language are you writing it in?
John: I'm writing it in C#.
Jane: I'm writing it in Python.
John: That's cool. I need to learn Python.
Jane: I need to learn C#.
John: Can I try out your chatbot?
Jane: Sure, here's the link.
John: Thanks!
Jane: You're welcome.
Jane: Look at this poem my chatbot wrote:
Jane: Roses are red
Jane: Violets are blue
Jane: I'm writing a chatbot
Jane: What about you?
John: That's cool. Let me see if mine will write a poem, too.
John: Here's a poem my chatbot wrote:
John: The signularity of the universe is a mystery.
Jane: You might want to try using a different model.
John: I'm using the GPT-2 model. That makes sense.
John: Here is a new poem after updating the model.
John: The universe is a mystery.
John: The universe is a mystery.
John: The universe is a mystery.
Jane: Sure, what's the problem?
John: Thanks for the help!
Jane: I'm now writing a bot to summarize conversations.
Jane: I have some bad news, we're only half way there.
John: Maybe there is a large piece of text we can use to generate a long conversation.
Jane: That's a good idea. Let me see if I can find one. Maybe Lorem Ipsum?
John: Yeah, that's a good idea."""

conversationSummarySkill = kernel.import_skill(
ConversationSummarySkill(kernel), "conversationSummary"
)
summary = await kernel.run_async(
conversationSummarySkill["SummarizeConversation"], input_str=ChatTranscript
)

output = str(summary).strip().lower()
print(output)
assert "john" in output and "jane" in output
assert len(output) < len(ChatTranscript)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio
import os

import e2e_text_completion
import pytest

import semantic_kernel as sk
import semantic_kernel.connectors.ai.open_ai as sk_oai


@pytest.mark.asyncio
async def test_azure_summarize_conversation_using_skill():
kernel = sk.Kernel()

if "Python_Integration_Tests" in os.environ:
deployment_name = os.environ["AzureOpenAI__DeploymentName"]
api_key = os.environ["AzureOpenAI__ApiKey"]
endpoint = os.environ["AzureOpenAI__Endpoint"]
else:
# Load credentials from .env file
deployment_name, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
deployment_name = "text-davinci-003"

kernel.add_text_completion_service(
"text_completion",
sk_oai.AzureTextCompletion(deployment_name, endpoint, api_key),
)

await e2e_text_completion.summarize_conversation_using_skill(kernel)


@pytest.mark.asyncio
async def test_oai_summarize_conversation_using_skill():
kernel = sk.Kernel()

if "Python_Integration_Tests" in os.environ:
api_key = os.environ["OpenAI__ApiKey"]
org_id = None
else:
# Load credentials from .env file
api_key, org_id = sk.openai_settings_from_dot_env()

kernel.add_chat_service(
"davinci-003",
sk_oai.OpenAITextCompletion("text-davinci-003", api_key, org_id=org_id),
)

await e2e_text_completion.summarize_conversation_using_skill(kernel)


if __name__ == "__main__":
asyncio.run(test_oai_summarize_conversation_using_skill())
asyncio.run(test_azure_summarize_conversation_using_skill())

0 comments on commit 9fccbe4

Please sign in to comment.