chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from semantic_kernel import Kernel
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
|
||||
from semantic_kernel.functions import kernel_function
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step import KernelProcessStep
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_context import KernelProcessStepContext
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
|
||||
from semantic_kernel.processes.local_runtime.local_event import KernelProcessEvent
|
||||
from semantic_kernel.processes.local_runtime.local_kernel_process import start
|
||||
from semantic_kernel.processes.process_builder import ProcessBuilder
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
|
||||
class CommonEvents(Enum):
|
||||
"""Common events for the sample process."""
|
||||
|
||||
UserInputReceived = "UserInputReceived"
|
||||
CompletionResponseGenerated = "CompletionResponseGenerated"
|
||||
WelcomeDone = "WelcomeDone"
|
||||
AStepDone = "AStepDone"
|
||||
BStepDone = "BStepDone"
|
||||
CStepDone = "CStepDone"
|
||||
StartARequested = "StartARequested"
|
||||
StartBRequested = "StartBRequested"
|
||||
ExitRequested = "ExitRequested"
|
||||
StartProcess = "StartProcess"
|
||||
|
||||
|
||||
# Define a sample step that once the `on_input_event` is received,
|
||||
# it will emit two events to start the A and B steps.
|
||||
class KickOffStep(KernelProcessStep):
|
||||
KICK_OFF_FUNCTION: ClassVar[str] = "kick_off"
|
||||
|
||||
@kernel_function(name=KICK_OFF_FUNCTION)
|
||||
async def print_welcome_message(self, context: KernelProcessStepContext):
|
||||
await context.emit_event(process_event=CommonEvents.StartARequested, data="Get Going A")
|
||||
await context.emit_event(process_event=CommonEvents.StartBRequested, data="Get Going B")
|
||||
|
||||
|
||||
# Define a sample `AStep` step that will emit an event after 1 second.
|
||||
# The event will be sent to the `CStep` step with the data `I did A`.
|
||||
class AStep(KernelProcessStep):
|
||||
@kernel_function()
|
||||
async def do_it(self, context: KernelProcessStepContext):
|
||||
await asyncio.sleep(1)
|
||||
await context.emit_event(process_event=CommonEvents.AStepDone, data="I did A")
|
||||
|
||||
|
||||
# Define a sample `BStep` step that will emit an event after 2 seconds.
|
||||
# The event will be sent to the `CStep` step with the data `I did B`.
|
||||
class BStep(KernelProcessStep):
|
||||
@kernel_function()
|
||||
async def do_it(self, context: KernelProcessStepContext):
|
||||
await asyncio.sleep(2)
|
||||
await context.emit_event(process_event=CommonEvents.BStepDone, data="I did B")
|
||||
|
||||
|
||||
# Define a sample `CStepState` that will keep track of the current cycle.
|
||||
class CStepState(BaseModel):
|
||||
current_cycle: int = 0
|
||||
|
||||
|
||||
# Define a sample `CStep` step that will emit an `ExitRequested` event after 3 cycles.
|
||||
class CStep(KernelProcessStep[CStepState]):
|
||||
state: CStepState = Field(default_factory=CStepState)
|
||||
|
||||
# The activate method overrides the base class method to set the state in the step.
|
||||
async def activate(self, state: KernelProcessStepState[CStepState]):
|
||||
"""Activates the step and sets the state."""
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function()
|
||||
async def do_it(self, context: KernelProcessStepContext, astepdata: str, bstepdata: str):
|
||||
self.state.current_cycle += 1
|
||||
print(f"CStep Current Cycle: {self.state.current_cycle}")
|
||||
if self.state.current_cycle == 3:
|
||||
print("CStep Exit Requested")
|
||||
await context.emit_event(process_event=CommonEvents.ExitRequested)
|
||||
return
|
||||
await context.emit_event(process_event=CommonEvents.CStepDone)
|
||||
|
||||
|
||||
kernel = Kernel()
|
||||
|
||||
|
||||
async def cycles_with_fan_in():
|
||||
kernel.add_service(OpenAIChatCompletion(service_id="default"))
|
||||
|
||||
# Define the process builder
|
||||
process = ProcessBuilder(name="Test Process")
|
||||
|
||||
# Add the step types to the builder
|
||||
kickoff_step = process.add_step(step_type=KickOffStep)
|
||||
myAStep = process.add_step(step_type=AStep)
|
||||
myBStep = process.add_step(step_type=BStep)
|
||||
myCStep = process.add_step(step_type=CStep)
|
||||
|
||||
# Define the input event and where to send it to
|
||||
process.on_input_event(event_id=CommonEvents.StartProcess).send_event_to(target=kickoff_step)
|
||||
|
||||
# Define the process flow
|
||||
kickoff_step.on_event(event_id=CommonEvents.StartARequested).send_event_to(target=myAStep)
|
||||
kickoff_step.on_event(event_id=CommonEvents.StartBRequested).send_event_to(target=myBStep)
|
||||
myAStep.on_event(event_id=CommonEvents.AStepDone).send_event_to(target=myCStep, parameter_name="astepdata")
|
||||
|
||||
# Define the fan in behavior once both AStep and BStep are done
|
||||
myBStep.on_event(event_id=CommonEvents.BStepDone).send_event_to(target=myCStep, parameter_name="bstepdata")
|
||||
myCStep.on_event(event_id=CommonEvents.CStepDone).send_event_to(target=kickoff_step)
|
||||
myCStep.on_event(event_id=CommonEvents.ExitRequested).stop_process()
|
||||
|
||||
# Build the process
|
||||
kernel_process = process.build()
|
||||
|
||||
async with await start(
|
||||
process=kernel_process,
|
||||
kernel=kernel,
|
||||
initial_event=KernelProcessEvent(id=CommonEvents.StartProcess, data="foo"),
|
||||
) as process_context:
|
||||
process_state = await process_context.get_state()
|
||||
c_step_state: KernelProcessStepState[CStepState] = next(
|
||||
(s.state for s in process_state.steps if s.state.name == "CStep"), None
|
||||
)
|
||||
assert c_step_state.state # nosec
|
||||
assert c_step_state.state.current_cycle == 3 # nosec
|
||||
print(f"Final State Check: CStepState current cycle: {c_step_state.state.current_cycle}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(cycles_with_fan_in())
|
||||
@@ -0,0 +1,119 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from semantic_kernel import Kernel
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
|
||||
from semantic_kernel.functions import kernel_function
|
||||
from semantic_kernel.processes.kernel_process.kernel_process import KernelProcess
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_event import KernelProcessEventVisibility
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step import KernelProcessStep
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_context import KernelProcessStepContext
|
||||
from semantic_kernel.processes.kernel_process.kernel_process_step_state import KernelProcessStepState
|
||||
from semantic_kernel.processes.local_runtime.local_kernel_process import start
|
||||
from semantic_kernel.processes.process_builder import ProcessBuilder
|
||||
from semantic_kernel.processes.process_types import TState
|
||||
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
|
||||
class ProcessEvents(Enum):
|
||||
StartProcess = "StartProcess"
|
||||
StartInnerProcess = "StartInnerProcess"
|
||||
OutputReadyPublic = "OutputReadyPublic"
|
||||
OutputReadyInternal = "OutputReadyInternal"
|
||||
|
||||
|
||||
class StepState(BaseModel):
|
||||
last_message: str = None
|
||||
|
||||
|
||||
class EchoStep(KernelProcessStep):
|
||||
ECHO: ClassVar[str] = "echo"
|
||||
|
||||
@kernel_function(name=ECHO)
|
||||
async def echo(self, message: str):
|
||||
print(f"[ECHO] {message}")
|
||||
return message
|
||||
|
||||
|
||||
class RepeatStep(KernelProcessStep[StepState]):
|
||||
REPEAT: ClassVar[str] = "repeat"
|
||||
|
||||
state: StepState = Field(default_factory=StepState)
|
||||
|
||||
async def activate(self, state: KernelProcessStepState[TState]):
|
||||
"""Activates the step and sets the state."""
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function(name=REPEAT)
|
||||
async def repeat(self, message: str, context: KernelProcessStepContext, count: int = 2):
|
||||
output = " ".join([message] * count)
|
||||
self.state.last_message = output
|
||||
print(f"[REPEAT] {output}")
|
||||
|
||||
await context.emit_event(
|
||||
process_event=ProcessEvents.OutputReadyPublic,
|
||||
data=output,
|
||||
visibility=KernelProcessEventVisibility.Public,
|
||||
)
|
||||
await context.emit_event(
|
||||
process_event=ProcessEvents.OutputReadyInternal,
|
||||
data=output,
|
||||
visibility=KernelProcessEventVisibility.Internal,
|
||||
)
|
||||
|
||||
|
||||
def create_linear_process(name: str):
|
||||
process_builder = ProcessBuilder(name=name)
|
||||
echo_step = process_builder.add_step(step_type=EchoStep)
|
||||
repeat_step = process_builder.add_step(step_type=RepeatStep)
|
||||
|
||||
process_builder.on_input_event(event_id=ProcessEvents.StartProcess).send_event_to(target=echo_step)
|
||||
|
||||
echo_step.on_function_result(function_name=EchoStep.ECHO).send_event_to(
|
||||
target=repeat_step, parameter_name="message"
|
||||
)
|
||||
|
||||
return process_builder
|
||||
|
||||
|
||||
kernel = Kernel()
|
||||
|
||||
|
||||
async def nested_process():
|
||||
kernel.add_service(OpenAIChatCompletion(service_id="default"))
|
||||
|
||||
process_builder = create_linear_process("Outer")
|
||||
|
||||
nested_process_step = process_builder.add_step_from_process(create_linear_process("Inner"))
|
||||
|
||||
process_builder.steps[1].on_event(ProcessEvents.OutputReadyInternal).send_event_to(
|
||||
nested_process_step.where_input_event_is(ProcessEvents.StartProcess)
|
||||
)
|
||||
|
||||
process = process_builder.build()
|
||||
|
||||
test_input = "Test"
|
||||
|
||||
process_handle = await start(
|
||||
process=process, kernel=kernel, initial_event=ProcessEvents.StartProcess, data=test_input
|
||||
)
|
||||
process_info = await process_handle.get_state()
|
||||
|
||||
inner_process: KernelProcess = next((s for s in process_info.steps if s.state.name == "Inner"), None)
|
||||
|
||||
repeat_step_state: KernelProcessStepState[StepState] = next(
|
||||
(s.state for s in inner_process.steps if s.state.name == "RepeatStep"), None
|
||||
)
|
||||
assert repeat_step_state.state # nosec
|
||||
assert repeat_step_state.state.last_message == "Test Test Test Test" # nosec
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(nested_process())
|
||||
@@ -0,0 +1,411 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import ClassVar
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from semantic_kernel import Kernel
|
||||
from semantic_kernel.agents import OpenAIResponsesAgent
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAISettings
|
||||
from semantic_kernel.functions import kernel_function
|
||||
from semantic_kernel.processes import ProcessBuilder
|
||||
from semantic_kernel.processes.kernel_process import (
|
||||
KernelProcess,
|
||||
KernelProcessEvent,
|
||||
KernelProcessEventVisibility,
|
||||
KernelProcessStep,
|
||||
KernelProcessStepContext,
|
||||
KernelProcessStepState,
|
||||
)
|
||||
from semantic_kernel.processes.local_runtime.local_kernel_process import start as start_local_process
|
||||
|
||||
"""
|
||||
The following code demonstrates a simple plan and execute process using the Semantic Kernel Process Framework.
|
||||
It defines a multi-step workflow that leverages OpenAI's Responses API and integrated web search tools to generate,
|
||||
refine, and execute a plan based on a user's query. An OpenAI api key is required to run this sample. The Azure OpenAI
|
||||
Responses API does not yet support the web search tool.
|
||||
|
||||
The process is composed of several steps:
|
||||
|
||||
- PlannerStep: Generates an initial plan (a set of tasks) in response to the user's request.
|
||||
- ReplanStep: Refines the plan when the initial tasks yield ambiguous or incomplete results.
|
||||
- ExecuteStep: Sequentially executes each task in the plan, incorporating previously gathered partial results to
|
||||
provide context.
|
||||
- DecisionStep: Evaluates progress and decides whether to continue executing the remaining tasks or to replan
|
||||
if necessary.
|
||||
- OutputStep: Aggregates and displays the final result or intermediate outputs to the user.
|
||||
|
||||
Each step communicates via events within the Semantic Kernel framework, ensuring that state and context are maintained
|
||||
throughout the process. A helper function centralizes the setup of the OpenAIResponsesAgent.
|
||||
|
||||
This example illustrates how to build an extensible, asynchronous processing pipeline capable of dynamically handling
|
||||
user queries and providing detailed outputs.
|
||||
"""
|
||||
|
||||
|
||||
#
|
||||
# 1) Helper to run OpenAI agent
|
||||
#
|
||||
async def run_openai_agent(instructions: str, prompt: str, agent_name: str = "GenericAgent") -> str:
|
||||
client = OpenAIResponsesAgent.create_client()
|
||||
agent_tools = [OpenAIResponsesAgent.configure_web_search_tool()]
|
||||
|
||||
agent = OpenAIResponsesAgent(
|
||||
ai_model_id=OpenAISettings().responses_model_id,
|
||||
client=client,
|
||||
instructions=instructions,
|
||||
name=agent_name,
|
||||
tools=agent_tools,
|
||||
)
|
||||
response = await agent.get_response(messages=prompt)
|
||||
return response.message.content.strip()
|
||||
|
||||
|
||||
#
|
||||
# 2) Global Events
|
||||
#
|
||||
class PlanExecuteEvents(str, Enum):
|
||||
StartProcess = "StartProcess"
|
||||
PlanCreated = "PlanCreated"
|
||||
PlanRefined = "PlanRefined"
|
||||
ExecuteNext = "ExecuteNext"
|
||||
TaskExecuted = "TaskExecuted"
|
||||
ReplanNeeded = "ReplanNeeded"
|
||||
PlanFinished = "PlanFinished"
|
||||
|
||||
|
||||
#
|
||||
# 3) Planner Step
|
||||
#
|
||||
class PlannerStepState(BaseModel):
|
||||
times_called: int = 0
|
||||
|
||||
|
||||
class PlannerStep(KernelProcessStep[PlannerStepState]):
|
||||
CREATE_PLAN: ClassVar[str] = "create_plan"
|
||||
state: PlannerStepState = Field(default_factory=PlannerStepState)
|
||||
|
||||
async def activate(self, state: KernelProcessStepState[PlannerStepState]):
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function(name=CREATE_PLAN)
|
||||
async def create_plan(self, user_request: str, context: KernelProcessStepContext) -> dict:
|
||||
self.state.times_called += 1
|
||||
prompt = (
|
||||
f"The user wants:\n{user_request}\n"
|
||||
"Propose a short plan with 2 or 3 bullet points for how to solve it. Keep them concise."
|
||||
"Keep citations as part of the plan.\n\n"
|
||||
)
|
||||
|
||||
response = await run_openai_agent(
|
||||
instructions="You generate short tasks to solve the user's request. Use any tool if relevant.",
|
||||
prompt=prompt,
|
||||
agent_name="PlanAgent",
|
||||
)
|
||||
plan_lines = response.strip().split("\n")
|
||||
|
||||
tasks = [line.lstrip("-* ").strip() for line in plan_lines if line.strip()]
|
||||
print(f"[PlannerStep] Created plan: {tasks} (times_called={self.state.times_called})")
|
||||
|
||||
return {"plan": tasks}
|
||||
|
||||
|
||||
#
|
||||
# 4) Replan Step
|
||||
#
|
||||
class ReplanStepState(BaseModel):
|
||||
times_called: int = 0
|
||||
|
||||
|
||||
class ReplanStep(KernelProcessStep[ReplanStepState]):
|
||||
REFINE_PLAN: ClassVar[str] = "refine_plan"
|
||||
state: ReplanStepState = Field(default_factory=ReplanStepState)
|
||||
|
||||
async def activate(self, state: KernelProcessStepState[ReplanStepState]):
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function(name=REFINE_PLAN)
|
||||
async def refine_plan(self, payload: dict, context: KernelProcessStepContext) -> dict:
|
||||
leftover = payload.get("leftover", [])
|
||||
reason = payload.get("reason", "")
|
||||
self.state.times_called += 1
|
||||
prompt = (
|
||||
f"Leftover tasks:\n{leftover}\n"
|
||||
f"Reason for replanning: {reason}\n"
|
||||
"Propose an updated set of tasks as bullet points."
|
||||
)
|
||||
response = await run_openai_agent(
|
||||
instructions="Refine or replan tasks for final answer.",
|
||||
prompt=prompt,
|
||||
agent_name="ReplanAgent",
|
||||
)
|
||||
plan_lines = response.strip().split("\n")
|
||||
new_plan = [line.lstrip("-* ").strip() for line in plan_lines if line.strip()]
|
||||
print(f"[ReplanStep] New plan: {new_plan} (times_called={self.state.times_called})")
|
||||
|
||||
return {"plan": new_plan}
|
||||
|
||||
|
||||
#
|
||||
# 5) Execute Step
|
||||
#
|
||||
class ExecuteStepState(BaseModel):
|
||||
current_index: int = 0
|
||||
|
||||
|
||||
class ExecuteStep(KernelProcessStep[ExecuteStepState]):
|
||||
EXECUTE_PLAN: ClassVar[str] = "execute_plan"
|
||||
state: ExecuteStepState = Field(default_factory=ExecuteStepState)
|
||||
|
||||
async def activate(self, state: KernelProcessStepState[ExecuteStepState]):
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function(name=EXECUTE_PLAN)
|
||||
async def execute_plan(self, payload: dict, context: KernelProcessStepContext) -> dict:
|
||||
plan = payload["plan"]
|
||||
partials = payload.get("partials", [])
|
||||
|
||||
if self.state.current_index >= len(plan):
|
||||
return {
|
||||
"partial_result": "All tasks done",
|
||||
"plan": plan,
|
||||
"current_index": self.state.current_index,
|
||||
}
|
||||
|
||||
current_task = plan[self.state.current_index]
|
||||
prompt = (
|
||||
f"So far we have these partial results:\n\n{chr(10).join(partials)}\n\nNow your task is: {current_task}"
|
||||
)
|
||||
|
||||
response = await run_openai_agent(
|
||||
instructions="Use the partial results and any relevant tools to complete the next step.",
|
||||
prompt=prompt,
|
||||
agent_name="ExecuteAgent",
|
||||
)
|
||||
partial_result = response.strip()
|
||||
|
||||
executed_index = self.state.current_index
|
||||
self.state.current_index += 1
|
||||
|
||||
return {
|
||||
"partial_result": partial_result,
|
||||
"plan": plan,
|
||||
"current_index": executed_index,
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# 6) Decision Step
|
||||
#
|
||||
class DecisionStepState(BaseModel):
|
||||
partials: list[str] = Field(default_factory=list)
|
||||
last_decision: str = ""
|
||||
|
||||
|
||||
class DecisionStep(KernelProcessStep[DecisionStepState]):
|
||||
MAKE_DECISION: ClassVar[str] = "make_decision"
|
||||
state: DecisionStepState = Field(default_factory=DecisionStepState)
|
||||
|
||||
async def activate(self, state: KernelProcessStepState[DecisionStepState]):
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function(name=MAKE_DECISION)
|
||||
async def make_decision(self, data: dict, context: KernelProcessStepContext):
|
||||
partial_result = data.get("partial_result", "")
|
||||
plan = data.get("plan", [])
|
||||
current_index = data.get("current_index", 0)
|
||||
|
||||
# Accumulate partial
|
||||
if partial_result and partial_result.lower() != "all tasks done":
|
||||
self.state.partials.append(partial_result)
|
||||
|
||||
# A) If "All tasks done"
|
||||
if partial_result.strip().lower().startswith("all tasks done"):
|
||||
# No more tasks
|
||||
final_text = "\n".join(self.state.partials)
|
||||
payload = {
|
||||
"type": "final",
|
||||
"content": final_text.strip(),
|
||||
}
|
||||
await context.emit_event(PlanExecuteEvents.PlanFinished, data=payload)
|
||||
return
|
||||
|
||||
# B) If physically done all tasks
|
||||
if current_index >= len(plan):
|
||||
final_text = "\n".join(self.state.partials)
|
||||
payload = {
|
||||
"type": "final",
|
||||
"content": final_text.strip(),
|
||||
}
|
||||
await context.emit_event(PlanExecuteEvents.PlanFinished, data=payload)
|
||||
return
|
||||
|
||||
# C) Otherwise: let LLM decide whether to continue or replan
|
||||
prompt = (
|
||||
"We have a plan with remaining tasks.\n"
|
||||
"PARTIAL RESULTS SO FAR:\n" + "\n".join(self.state.partials) + "\n\n"
|
||||
"Decide: 'continue' with the next step or 'replan' if something is invalid. "
|
||||
"DO NOT say 'finish' if tasks remain. Return only 'continue' or 'replan'."
|
||||
)
|
||||
response = await run_openai_agent(
|
||||
instructions="Plan orchestrator with web-search if needed. Only respond 'continue' or 'replan'.",
|
||||
prompt=prompt,
|
||||
agent_name="DecisionAgent",
|
||||
)
|
||||
|
||||
decision = response.strip().lower()
|
||||
print(f"[DecisionStep] LLM decision: {decision}")
|
||||
self.state.last_decision = decision
|
||||
|
||||
# If replan => emit ReplanNeeded
|
||||
if "replan" in decision:
|
||||
leftover = plan[current_index:]
|
||||
payload = {"leftover": leftover, "reason": partial_result}
|
||||
await context.emit_event(PlanExecuteEvents.ReplanNeeded, data=payload)
|
||||
return
|
||||
|
||||
# Default => continue
|
||||
await context.emit_event(
|
||||
PlanExecuteEvents.ExecuteNext,
|
||||
data={
|
||||
"plan": plan,
|
||||
"partials": self.state.partials,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# 7) Output Step
|
||||
#
|
||||
class OutputStepState(BaseModel):
|
||||
debug_history: list[str] = Field(default_factory=list)
|
||||
final_answer: str = ""
|
||||
|
||||
|
||||
class OutputStep(KernelProcessStep[OutputStepState]):
|
||||
SHOW_MESSAGE: ClassVar[str] = "show_message"
|
||||
state: OutputStepState = Field(default_factory=OutputStepState)
|
||||
|
||||
async def activate(self, state: KernelProcessStepState[OutputStepState]):
|
||||
self.state = state.state
|
||||
|
||||
@kernel_function(name=SHOW_MESSAGE)
|
||||
async def show_message(self, message: dict):
|
||||
"""Handles either debug messages or final messages."""
|
||||
msg_type = message.get("type", "debug")
|
||||
content = message.get("content", "")
|
||||
|
||||
if msg_type == "debug":
|
||||
self.state.debug_history.append(content)
|
||||
print(content)
|
||||
else:
|
||||
# final
|
||||
self.state.final_answer = content
|
||||
print("[OutputStep] Storing final result:", content)
|
||||
|
||||
|
||||
#
|
||||
# 8) Build the Process
|
||||
#
|
||||
def build_process() -> KernelProcess:
|
||||
builder = ProcessBuilder(name="GeneralPlanAndExecute")
|
||||
|
||||
# Steps
|
||||
planner = builder.add_step(PlannerStep)
|
||||
replan = builder.add_step(ReplanStep)
|
||||
executor = builder.add_step(ExecuteStep)
|
||||
decider = builder.add_step(DecisionStep)
|
||||
output = builder.add_step(OutputStep)
|
||||
|
||||
# 1) Start => Planner
|
||||
builder.on_input_event(PlanExecuteEvents.StartProcess).send_event_to(target=planner, parameter_name="user_request")
|
||||
|
||||
# 2) Planner => Executor + Output (debug)
|
||||
planner.on_function_result(PlannerStep.CREATE_PLAN).send_event_to(target=executor, parameter_name="payload")
|
||||
|
||||
planner.on_function_result(PlannerStep.CREATE_PLAN).send_event_to(
|
||||
target=output,
|
||||
parameter_name="message",
|
||||
)
|
||||
|
||||
# 3) Executor => Decision
|
||||
executor.on_function_result(ExecuteStep.EXECUTE_PLAN).send_event_to(target=decider, parameter_name="data")
|
||||
|
||||
# 4) Decision => (ExecuteNext, ReplanNeeded, PlanFinished)
|
||||
decider.on_event(PlanExecuteEvents.ExecuteNext).send_event_to(target=executor, parameter_name="payload")
|
||||
decider.on_event(PlanExecuteEvents.ReplanNeeded).send_event_to(target=replan, parameter_name="payload")
|
||||
decider.on_event(PlanExecuteEvents.PlanFinished).send_event_to(target=output, parameter_name="message")
|
||||
decider.on_event(PlanExecuteEvents.PlanFinished).stop_process()
|
||||
|
||||
# 5) Replan => Executor
|
||||
replan.on_function_result(ReplanStep.REFINE_PLAN).send_event_to(target=executor, parameter_name="payload")
|
||||
|
||||
return builder.build()
|
||||
|
||||
|
||||
async def main():
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
|
||||
# Provide any user question
|
||||
user_question = "Where was the quarterback of the winning team in the 2014 Super Bowl born?"
|
||||
print(f"Starting process with: '{user_question}'")
|
||||
|
||||
process = build_process()
|
||||
async with await start_local_process(
|
||||
process=process,
|
||||
kernel=Kernel(),
|
||||
initial_event=KernelProcessEvent(
|
||||
id=PlanExecuteEvents.StartProcess,
|
||||
data=user_question,
|
||||
visibility=KernelProcessEventVisibility.Public,
|
||||
),
|
||||
) as process_context:
|
||||
# Retrieve final state
|
||||
process_state = await process_context.get_state()
|
||||
output_step_state: KernelProcessStepState[OutputStepState] = next(
|
||||
(s.state for s in process_state.steps if s.state.name == "OutputStep"), None
|
||||
)
|
||||
|
||||
if output_step_state:
|
||||
# Final user-facing answer:
|
||||
final_answer = output_step_state.state.final_answer.strip()
|
||||
|
||||
print("\n[Final State]:")
|
||||
print(final_answer)
|
||||
else:
|
||||
print("[Final State]: No final message.")
|
||||
|
||||
"""
|
||||
Starting process with: 'Where was the quarterback of the winning team in the 2014 Super Bowl born?'
|
||||
[PlannerStep] Created plan: ['Identify the Winning Team:** Find out which team won the 2014 Super Bowl.
|
||||
[NFL.com](https://www.nfl.com/)', 'Locate the Quarterback:** Determine who was the quarterback for the winning
|
||||
team during that game.', 'Research Birthplace:** Search for the birthplace of the identified quarterback.
|
||||
[Wikipedia](https://www.wikipedia.org/)'] (times_called=1)
|
||||
|
||||
[DecisionStep] LLM decision: continue
|
||||
[DecisionStep] LLM decision: continue
|
||||
[DecisionStep] LLM decision: continue
|
||||
[OutputStep] Storing final result: The Seattle Seahawks won Super Bowl XLVIII in 2014, defeating the Denver Broncos
|
||||
43-8. The Seahawks' defense dominated the game, forcing four turnovers and limiting the Broncos' high-scoring
|
||||
offense. Linebacker Malcolm Smith was named Super Bowl MVP after returning an interception 69 yards for a
|
||||
touchdown. ([nfl.com](https://www.nfl.com/news/seattle-seahawks-d-dominates-manning-denver-broncos-to-win-supe-0ap2000000323056?utm_source=openai))
|
||||
The quarterback for the Seattle Seahawks during Super Bowl XLVIII was Russell Wilson.
|
||||
Russell Wilson, the quarterback for the Seattle Seahawks during Super Bowl XLVIII, was born on November 29, 1988,
|
||||
in Cincinnati, Ohio. ([britannica.com](https://www.britannica.com/facts/Russell-Wilson?utm_source=openai))
|
||||
|
||||
[Final State]:
|
||||
The Seattle Seahawks won Super Bowl XLVIII in 2014, defeating the Denver Broncos 43-8. The Seahawks' defense
|
||||
dominated the game, forcing four turnovers and limiting the Broncos' high-scoring offense. Linebacker Malcolm
|
||||
Smith was named Super Bowl MVP after returning an interception 69 yards for a touchdown.
|
||||
([nfl.com](https://www.nfl.com/news/seattle-seahawks-d-dominates-manning-denver-broncos-to-win-supe-0ap2000000323056?utm_source=openai))
|
||||
The quarterback for the Seattle Seahawks during Super Bowl XLVIII was Russell Wilson.
|
||||
Russell Wilson, the quarterback for the Seattle Seahawks during Super Bowl XLVIII, was born on November 29, 1988,
|
||||
in Cincinnati, Ohio. ([britannica.com](https://www.britannica.com/facts/Russell-Wilson?utm_source=openai))
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user