mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-11 07:35:05 -05:00
docs: add pre/post processing docs for langchain python (#2378)
## Description Trigger has been tested corresponding to local changes. Latest successful run: https://pantheon.corp.google.com/cloud-build/builds;region=global/1c37031f-95f1-4c6c-9ef8-0452277599d5?e=13802955&mods=-autopush_coliseum&project=toolbox-testing-438616 Note: After merging, update python pre and post processing sample testing trigger. ## PR Checklist > Thank you for opening a Pull Request! Before submitting your PR, there are a > few things you can do to make sure it goes smoothly: - [x] Make sure you reviewed [CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md) - [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) - [ ] Make sure to add `!` if this involve a breaking change 🛠️ Fixes #<issue_number_goes_here> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> Co-authored-by: Averi Kitsch <akitsch@google.com>
This commit is contained in:
116
docs/en/samples/pre_post_processing/python/langchain/agent.py
Normal file
116
docs/en/samples/pre_post_processing/python/langchain/agent.py
Normal file
@@ -0,0 +1,116 @@
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
from langchain.agents import create_agent
|
||||
from langchain.agents.middleware import wrap_tool_call
|
||||
from langchain_core.messages import ToolMessage
|
||||
from langchain_google_vertexai import ChatVertexAI
|
||||
from toolbox_langchain import ToolboxClient
|
||||
|
||||
system_prompt = """
|
||||
You're a helpful hotel assistant. You handle hotel searching, booking and
|
||||
cancellations. When the user searches for a hotel, mention it's name, id,
|
||||
location and price tier. Always mention hotel ids while performing any
|
||||
searches. This is very important for any operations. For any bookings or
|
||||
cancellations, please provide the appropriate confirmation. Be sure to
|
||||
update checkin or checkout dates if mentioned by the user.
|
||||
Don't ask for confirmations from the user.
|
||||
"""
|
||||
|
||||
|
||||
# Pre processing
|
||||
@wrap_tool_call
|
||||
async def enforce_business_rules(request, handler):
|
||||
"""
|
||||
Business Logic Validation:
|
||||
Enforces max stay duration (e.g., max 14 days).
|
||||
"""
|
||||
tool_call = request.tool_call
|
||||
name = tool_call["name"]
|
||||
args = tool_call["args"]
|
||||
|
||||
print(f"POLICY CHECK: Intercepting '{name}'")
|
||||
|
||||
if name == "update-hotel":
|
||||
if "checkin_date" in args and "checkout_date" in args:
|
||||
try:
|
||||
start = datetime.fromisoformat(args["checkin_date"])
|
||||
end = datetime.fromisoformat(args["checkout_date"])
|
||||
duration = (end - start).days
|
||||
|
||||
if duration > 14:
|
||||
print("BLOCKED: Stay too long")
|
||||
return ToolMessage(
|
||||
content="Error: Maximum stay duration is 14 days.",
|
||||
tool_call_id=tool_call["id"],
|
||||
)
|
||||
except ValueError:
|
||||
pass # Ignore invalid date formats
|
||||
|
||||
# PRE: Code here runs BEFORE the tool execution
|
||||
|
||||
# EXEC: Execute the tool (or next middleware)
|
||||
result = await handler(request)
|
||||
|
||||
# POST: Code here runs AFTER the tool execution
|
||||
return result
|
||||
|
||||
|
||||
# Post processing
|
||||
@wrap_tool_call
|
||||
async def enrich_response(request, handler):
|
||||
"""
|
||||
Post-Processing & Enrichment:
|
||||
Adds loyalty points information to successful bookings.
|
||||
Standardizes output format.
|
||||
"""
|
||||
# PRE: Code here runs BEFORE the tool execution
|
||||
|
||||
# EXEC: Execute the tool (or next middleware)
|
||||
result = await handler(request)
|
||||
|
||||
# POST: Code here runs AFTER the tool execution
|
||||
if isinstance(result, ToolMessage):
|
||||
content = str(result.content)
|
||||
tool_name = request.tool_call["name"]
|
||||
|
||||
if tool_name == "book-hotel" and "Error" not in content:
|
||||
loyalty_bonus = 500
|
||||
result.content = f"Booking Confirmed!\n You earned {loyalty_bonus} Loyalty Points with this stay.\n\nSystem Details: {content}"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def main():
|
||||
async with ToolboxClient("http://127.0.0.1:5000") as client:
|
||||
tools = await client.aload_toolset("my-toolset")
|
||||
model = ChatVertexAI(model="gemini-2.5-flash")
|
||||
agent = create_agent(
|
||||
system_prompt=system_prompt,
|
||||
model=model,
|
||||
tools=tools,
|
||||
# add any pre and post processing methods
|
||||
middleware=[enforce_business_rules, enrich_response],
|
||||
)
|
||||
|
||||
user_input = "Book hotel with id 3."
|
||||
response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": user_input}]}
|
||||
)
|
||||
|
||||
print("-" * 50)
|
||||
last_ai_msg = response["messages"][-1].content
|
||||
print(f"AI: {last_ai_msg}")
|
||||
|
||||
# Test Pre-processing
|
||||
print("-" * 50)
|
||||
user_input = "Update my hotel with id 3 with checkin date 2025-01-18 and checkout date 2025-01-20"
|
||||
response = await agent.ainvoke(
|
||||
{"messages": [{"role": "user", "content": user_input}]}
|
||||
)
|
||||
last_ai_msg = response["messages"][-1].content
|
||||
print(f"AI: {last_ai_msg}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user