-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathbase.py
More file actions
146 lines (127 loc) · 5.4 KB
/
Copy pathbase.py
File metadata and controls
146 lines (127 loc) · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""Base agent for chat completion within a state flow context."""
import logging
from collections.abc import AsyncIterable
from semantic_kernel.kernel import Kernel
from semantic_kernel.agents import ChatCompletionAgent
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.chat_history import ChatHistory
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.exceptions import KernelServiceNotFoundError
logger: logging.Logger = logging.getLogger(__name__)
class StateFlowBaseAgent(ChatCompletionAgent):
"""
StateFlowBaseAgent is a specialized agent for handling chat completions
within a state flow context. It extends the ChatCompletionAgent and
provides additional functionality for invoking chat completion services
and managing chat history.
"""
def __init__(
self,
service_id: str | None = None,
kernel: Kernel | None = None,
name: str | None = None,
id: str | None = None,
description: str | None = None,
instructions: str | None = None,
execution_settings: PromptExecutionSettings | None = None,
):
"""
Initializes the StateFlowBaseAgent with the given parameters.
Args:
service_id (str | None): The ID of the chat completion service.
kernel (Kernel | None): The kernel instance to use for service retrieval.
name (str | None): The name of the agent.
id (str | None): The ID of the agent.
description (str | None): A description of the agent.
instructions (str | None): Instructions for the agent.
execution_settings (PromptExecutionSettings | None): Settings for prompt execution.
"""
super().__init__(
service_id=service_id,
kernel=kernel,
name=name,
id=id,
description=description,
instructions=instructions,
execution_settings=execution_settings,
)
async def invoke(self, history: ChatHistory) -> AsyncIterable[ChatMessageContent]:
"""
Asynchronously invokes the chat completion service with the provided chat history.
Args:
history (ChatHistory): The chat history to use for the invocation.
Raises:
KernelServiceNotFoundError: If the chat completion service is not found.
Yields:
AsyncIterable[ChatMessageContent]: The chat message contents as they are generated.
"""
# Get the chat completion service
chat_completion_service = self.kernel.get_service(
service_id=self.service_id, type=ChatCompletionClientBase
)
if not chat_completion_service:
raise KernelServiceNotFoundError(
f"Chat completion service not found with service_id: {self.service_id}"
)
assert isinstance(chat_completion_service, ChatCompletionClientBase) # nosec
settings = (
self.execution_settings
or self.kernel.get_prompt_execution_settings_from_service_id(
self.service_id
)
or chat_completion_service.instantiate_prompt_execution_settings(
service_id=self.service_id,
extension_data={"ai_model_id": chat_completion_service.ai_model_id},
)
)
chat = self._setup_agent_chat_history(history)
message_count = len(chat)
logger.debug(
"[%s] Invoking %s.",
type(self).__name__,
type(chat_completion_service).__name__,
)
messages = await chat_completion_service.get_chat_message_contents(
chat_history=chat,
settings=settings,
kernel=self.kernel,
)
logger.info(
"[%s] Invoked %s with message count: %d.",
type(self).__name__,
type(chat_completion_service).__name__,
message_count
)
# Verify if thought and action are generated
result = messages[-1]
thought_action = result.content.strip()
try:
a = thought_action.strip().split("Action: ")
thought, action = a[0], a[1]
except IndexError:
# Fail to split, assume last step is thought, call model again to get action assume last step is thought
thought = thought_action.strip()
if not "Thought:" in thought:
thought = f"Thought: {thought}"
chat[-1].content += f"\n{thought}\nAction: "
messages = await chat_completion_service.get_chat_message_contents(
chat_history=chat,
settings=settings,
kernel=self.kernel,
)
action = result.content.strip()
thought_action = f"{thought.strip()}\nAction: {action.strip()}"
# Capture mutated messages related function calling / tools
for message_index in range(message_count, len(chat)):
message = chat[message_index]
message.name = self.name
history.add_message(message)
for message in messages:
message.name = self.name
message.content = thought_action
yield message