Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip(agents-api): Auto-run tools in prompt steps #794

Draft
wants to merge 4 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 15 additions & 25 deletions agents-api/agents_api/autogen/Sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ class CreateSessionRequest(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -80,12 +78,10 @@ class PatchSessionRequest(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -117,12 +113,10 @@ class Session(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -190,12 +184,10 @@ class UpdateSessionRequest(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -234,12 +226,10 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down
16 changes: 6 additions & 10 deletions agents-api/agents_api/autogen/Tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,12 +702,10 @@ class PromptStep(BaseModel):
"""
Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content`
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = True
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: true for prompt steps, false for sessions)

If a tool call is made, the tool's output will be used as the model's input.
If a tool call is not made, the model's output will be used as the next step's input.
Expand Down Expand Up @@ -746,12 +744,10 @@ class PromptStepUpdateItem(BaseModel):
"""
Whether to unwrap the output of the prompt step, equivalent to `response.choices[0].message.content`
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = True
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: true for prompt steps, false for sessions)

If a tool call is made, the tool's output will be used as the model's input.
If a tool call is not made, the model's output will be used as the next step's input.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def create_or_update_session(
data: CreateOrUpdateSessionRequest,
) -> tuple[list[str], dict]:
data.metadata = data.metadata or {}
session_data = data.model_dump()
session_data = data.model_dump(exclude={"auto_run_tools"})

user = session_data.pop("user")
agent = session_data.pop("agent")
Expand Down
2 changes: 1 addition & 1 deletion agents-api/agents_api/models/session/create_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def create_session(
session_id = session_id or uuid4()

data.metadata = data.metadata or {}
session_data = data.model_dump()
session_data = data.model_dump(exclude={"auto_run_tools"})

user = session_data.pop("user")
agent = session_data.pop("agent")
Expand Down
108 changes: 101 additions & 7 deletions agents-api/agents_api/workflows/task_execution/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,23 @@ async def run(
state = PartialTransition(type="resume", output=result)

case PromptStep(unwrap=True), StepOutcome(output=response):
finish_reason = response["choices"][0]["finish_reason"]
if finish_reason == "tool_calls":
workflow.logger.error(
"Prompt step: Tool calls not supported in unwrap mode"
)

state = PartialTransition(
type="error", output="Tool calls not supported in unwrap mode"
)
await transition(context, state)

raise ApplicationError("Tool calls not supported in unwrap mode")

workflow.logger.debug(f"Prompt step: Received response: {response}")
state = PartialTransition(output=response)

case PromptStep(forward_tool_results=False, unwrap=False), StepOutcome(
case PromptStep(auto_run_tools=False, unwrap=False), StepOutcome(
output=response
):
workflow.logger.debug(f"Prompt step: Received response: {response}")
Expand All @@ -387,12 +400,22 @@ async def run(
workflow.logger.debug(f"Prompt step: Received response: {response}")
state = PartialTransition(output=response)

case PromptStep(unwrap=False), StepOutcome(output=response) if response[
"choices"
][0]["finish_reason"] == "tool_calls":
workflow.logger.debug("Prompt step: Received tool call")
message = response["choices"][0]["message"]
tool_calls_input = message["tool_calls"]
## TODO: Handle multiple tool calls and multiple choices
# case PromptStep(unwrap=False), StepOutcome(output=response) if response[
# "choices"
# ][0]["finish_reason"] == "tool_calls":
# workflow.logger.debug("Prompt step: Received tool call")
# message = response["choices"][0]["message"]
# tool_calls_input = message["tool_calls"]

case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome(
output=response
) if (message := response["choices"][0])[
"finish_reason"
] == "tool_calls" and (tool_calls_input := message["tool_calls"])[0][
"type"
] == "function":
workflow.logger.debug("Prompt step: Received FUNCTION tool call")

# Enter a wait-for-input step to ask the developer to run the tool calls
tool_calls_results = await workflow.execute_activity(
Expand All @@ -415,6 +438,67 @@ async def run(
)
state = PartialTransition(output=new_response.output, type="resume")

case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome(
output=response
) if (message := response["choices"][0])[
"finish_reason"
] == "tool_calls" and (tool_calls_input := message["tool_calls"])[0][
"type"
] == "integration":
workflow.logger.debug("Prompt step: Received INTEGRATION tool call")

# FIXME: Implement integration tool calls
# See: MANUAL TOOL CALL INTEGRATION (below)
raise NotImplementedError("Integration tool calls not yet supported")

# TODO: Feed the tool call results back to the model (see above)

case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome(
output=response
) if (message := response["choices"][0])[
"finish_reason"
] == "tool_calls" and (tool_calls_input := message["tool_calls"])[0][
"type"
] == "api_call":
workflow.logger.debug("Prompt step: Received API_CALL tool call")

# FIXME: Implement API_CALL tool calls
# See: MANUAL TOOL CALL API_CALL (below)
raise NotImplementedError("API_CALL tool calls not yet supported")

# TODO: Feed the tool call results back to the model (see above)

case PromptStep(auto_run_tools=True, unwrap=False), StepOutcome(
output=response
) if (message := response["choices"][0])[
"finish_reason"
] == "tool_calls" and (tool_calls_input := message["tool_calls"])[0][
"type"
] == "system":
workflow.logger.debug("Prompt step: Received SYSTEM tool call")

# FIXME: Implement SYSTEM tool calls
# See: MANUAL TOOL CALL SYSTEM (below)
raise NotImplementedError("SYSTEM tool calls not yet supported")

# TODO: Feed the tool call results back to the model (see above)

case PromptStep(unwrap=False), StepOutcome(output=response) if (
message := response["choices"][0]
)["finish_reason"] == "tool_calls" and (
tool_calls_input := message["tool_calls"]
)[0]["type"] not in ["function", "integration", "api_call", "system"]:
workflow.logger.debug(
f"Prompt step: Received unknown tool call: {tool_calls_input[0]['type']}"
)
state = PartialTransition(output=response)

case SetStep(), StepOutcome(output=evaluated_output):
workflow.logger.info("Set step: Updating user state")

case SetStep(), StepOutcome(output=evaluated_output):
workflow.logger.info("Set step: Updating user state")

case SetStep(), StepOutcome(output=evaluated_output):
workflow.logger.info("Set step: Updating user state")

Expand Down Expand Up @@ -452,6 +536,8 @@ async def run(
case ToolCallStep(), StepOutcome(output=tool_call) if tool_call[
"type"
] == "integration":
# MANUAL TOOL CALL INTEGRATION
workflow.logger.debug("ToolCallStep: Received INTEGRATION tool call")
call = tool_call["integration"]
tool_name = call["name"]
arguments = call["arguments"]
Expand Down Expand Up @@ -490,6 +576,8 @@ async def run(
case ToolCallStep(), StepOutcome(output=tool_call) if tool_call[
"type"
] == "api_call":
# MANUAL TOOL CALL API_CALL
workflow.logger.debug("ToolCallStep: Received API_CALL tool call")
call = tool_call["api_call"]
tool_name = call["name"]
arguments = call["arguments"]
Expand Down Expand Up @@ -528,6 +616,8 @@ async def run(
case ToolCallStep(), StepOutcome(output=tool_call) if tool_call[
"type"
] == "system":
# MANUAL TOOL CALL SYSTEM
workflow.logger.debug("ToolCallStep: Received SYSTEM tool call")
call = tool_call.get("system")

system_call = SystemDef(**call)
Expand All @@ -545,12 +635,16 @@ async def run(
workflow.logger.error(
f"Unhandled step type: {type(context.current_step).__name__}"
)
state = PartialTransition(type="error", output="Not implemented")
await transition(context, state)

raise ApplicationError("Not implemented")

# 4. Transition to the next step
workflow.logger.info(f"Transitioning after step {context.cursor.step}")

# The returned value is the transition finally created
state = state or PartialTransition(type="error", output="Not implemented")
final_state = await transition(context, state)

# ---
Expand Down
40 changes: 15 additions & 25 deletions integrations-service/integrations/autogen/Sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ class CreateSessionRequest(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -80,12 +78,10 @@ class PatchSessionRequest(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -117,12 +113,10 @@ class Session(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -190,12 +184,10 @@ class UpdateSessionRequest(BaseModel):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down Expand Up @@ -234,12 +226,10 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest):
"""
Action to start on context window overflow
"""
forward_tool_results: StrictBool | None = None
auto_run_tools: StrictBool = False
"""
Whether to forward the tool results to the model when available.
"true" => always forward
"false" => never forward
null => forward if applicable (default)
Whether to auto-run the tool and send the tool results to the model when available.
(default: false for sessions, true for tasks)

If a tool call is made, the tool's output will be sent back to the model as the model's input.
If a tool call is not made, the model's output will be returned as is.
Expand Down
Loading
Loading