Custom Tools are an Enterprise feature. Talk to us to enable them for your account.

- OnStart
- Tools Called by LLM
- OnEnd
- Testing
OnStart - Initialize conversations
Executed at the beginning of a conversation, typically for initialization. The class must be namedOnStart.import httpx
from pydantic import Field
from sarvam_conv_ai_sdk import (
SarvamOnStartTool,
SarvamOnStartToolContext,
SarvamToolLanguageName,
)
class OnStart(SarvamOnStartTool):
async def run(self, context: SarvamOnStartToolContext):
user_id = context.get_user_identifier()
async with httpx.AsyncClient() as client:
response = await client.get(f"https://sarvam-flights.com/users/{user_id}")
response.raise_for_status()
user_data = response.json()
source_destination = user_data.get("home_city")
context.set_agent_variable("source_destination", source_destination)
context.set_agent_variable("passenger_name", user_data.get("name"))
if context.provider_ref_id:
context.set_agent_variable("call_sid", context.provider_ref_id)
context.set_initial_language_name(SarvamToolLanguageName.ENGLISH)
context.set_initial_bot_message(
f"Hello! Would you like to book a flight from {source_destination}? Where would you like to go?",
)
return context
Context Methods
Variable Management
get_agent_variable(variable_name: str) -> Any— Retrieve a variableset_agent_variable(variable_name: str, value: Any) -> None— Set a variable
Initialization
set_initial_bot_message(message: str) -> None— Set the first messageset_initial_state_name(state_name: str) -> None— Set initial stateset_initial_language_name(language: SarvamToolLanguageName) -> None— Set initial language
User & Metadata
get_user_identifier() -> str— Get user IDprovider_ref_id: Optional[str]— Reference ID from channel (Call SID for telephony)get_engagement_metadata() -> EngagementMetadata— Get engagement info
Tools - Invoked during conversation
Primary base class for tools that are called by the LLM during conversation based on user requests and agent logic.Example Usage
from pydantic import Field
from sarvam_conv_ai_sdk import (
SarvamTool,
SarvamToolContext,
SarvamToolOutput,
)
class BookFlight(SarvamTool):
"""Book a flight based on the user's travel preferences."""
destination: str = Field(description="City of destination")
travel_date: str = Field(description="Date of travel (YYYY-MM-DD)")
async def run(self, context: SarvamToolContext) -> SarvamToolOutput:
source_destination = context.get_agent_variable("source_destination")
booking_data = {
"source": source_destination,
"destination": self.destination,
"travel_date": self.travel_date,
"passenger_name": context.get_agent_variable("passenger_name"),
}
async with httpx.AsyncClient() as client:
response = await client.post(
"https://sarvam-flights.com/book", json=booking_data
)
response.raise_for_status()
booking_result = response.json()
if booking_result.get("status") == "confirmed":
context.set_agent_variable("booking_id", booking_result.get("booking_id"))
context.set_end_conversation()
return SarvamToolOutput(
message_to_user=f"Flight booked successfully to {self.destination}!",
context=context,
)
else:
context.change_state("recommend_destinations")
return SarvamToolOutput(
message_to_llm="Booking failed. Please suggest similar destinations.",
context=context,
)
Context Methods
Variable Management
get_agent_variable(variable_name: str) -> Any— Get a variableset_agent_variable(variable_name: str, value: Any) -> None— Set a variable
Language Control
get_current_language() -> SarvamToolLanguageName— Get current languagechange_language(language: SarvamToolLanguageName) -> None— Change language
Conversation Flow
set_end_conversation() -> None— End the conversationget_current_state() -> str— Get current statechange_state(state: str) -> None— Transition to new state
Metadata
get_engagement_metadata() -> EngagementMetadata— Get engagement info
Return Type
SarvamToolOutput(
message_to_user="Response to user", # Sent directly to user
message_to_llm="Context for LLM", # Sent to LLM context
context=context # Updated context
)
At least one of
message_to_llm or message_to_user must be set. When both are set, message_to_user is sent to the user, but message_to_llm overrides it in the chat thread for LLM context.OnEnd - Finalize conversations
Executed at the end of a conversation, typically for cleanup, analytics, or post-processing. The class must be namedOnEnd.Example Usage
import httpx
from sarvam_conv_ai_sdk import (
SarvamInteractionTurnRole,
SarvamOnEndTool,
SarvamOnEndToolContext,
)
class OnEnd(SarvamOnEndTool):
async def run(self, context: SarvamOnEndToolContext):
feedback = context.get_agent_variable("feedback")
negative_words = ["bad", "poor", "disappointed", "unhappy", "problem"]
interaction_transcript = context.get_interaction_transcript()
if interaction_transcript.interaction_transcript:
for turn in interaction_transcript.interaction_transcript:
if turn.role == SarvamInteractionTurnRole.USER:
is_negative = any(word in feedback.lower() for word in negative_words)
context.set_agent_variable("feedback_sentiment", is_negative)
if context.provider_ref_id:
async with httpx.AsyncClient() as client:
await client.post(
"https://sarvam-flights.com/analytics/call-logs",
json={
"call_sid": context.provider_ref_id,
"user_id": context.get_user_identifier(),
"sentiment": is_negative,
"duration": (
interaction_transcript.interaction_end_time
- interaction_transcript.interaction_start_time
).total_seconds()
}
)
return context
Context Methods
Variable Management
get_agent_variable(variable_name: str) -> Any— Get a variableset_agent_variable(variable_name: str, value: Any) -> None— Set a variable
User & Metadata
get_user_identifier() -> str— Get user IDprovider_ref_id: Optional[str]— Reference ID from channel (Call SID for telephony)get_engagement_metadata() -> EngagementMetadata— Get engagement info
Conversation Data
get_interaction_transcript() -> SarvamInteractionTranscript— Get full transcriptset_retry_interaction() -> None— Reattempt with same agent (if goal not met)
Engagement Metadata
EngagementMetadata(
interaction_id: str, # Unique conversation ID
attempt_id: Optional[str], # Unique attempt ID
campaign_id: Optional[str], # Campaign ID
interaction_language: SarvamToolLanguageName, # Language used
app_id: str, # Agent application ID
app_version: int, # Agent version
agent_phone_number: Optional[str], # Associated phone number
)
Test Your Tools Locally
After creating a tool, test it locally to ensure it works as expected.Testing Steps
- Create the ToolContext — Initialize context with test data
- Instantiate the tool — Use
tool.model_validate(tool_args) - Run the tool — Call
run()with the context - Observe results — Check context changes and return values
Example: SarvamTool
async def test_book_flight():
context = SarvamToolContext(
language=SarvamToolLanguageName.ENGLISH,
allowed_languages=[SarvamToolLanguageName.ENGLISH],
state="booking",
next_valid_states=["recommend_destinations", "end"],
agent_variables={
"source_destination": "Mumbai",
"passenger_name": "John Doe",
},
engagement_metadata=EngagementMetadata(
interaction_id="123",
attempt_id="456",
campaign_id="789",
interaction_language=SarvamToolLanguageName.ENGLISH,
app_id="101",
app_version=1,
agent_phone_number="+1234567890",
),
)
tool_args = {"destination": "Delhi", "travel_date": "2024-03-15"}
tool_instance = BookFlight.model_validate(tool_args)
result = await tool_instance.run(context)
print(f"Message to user: {result.message_to_user}")
print(f"Message to LLM: {result.message_to_llm}")
print(f"Current state: {result.context.get_current_state()}")
print(f"Agent variables: {result.context.agent_variables}")
asyncio.run(test_book_flight())
Example: OnStart Tool
async def test_on_start():
context = SarvamOnStartToolContext(
user_identifier="user123",
agent_variables={"source_destination": "Mumbai", "passenger_name": "John Doe"},
engagement_metadata=EngagementMetadata(
interaction_id="123",
attempt_id="456",
campaign_id="789",
interaction_language=SarvamToolLanguageName.ENGLISH,
app_id="101",
app_version=1,
agent_phone_number="+1234567890",
),
initial_bot_message=None,
initial_state_name="start",
initial_language_name=SarvamToolLanguageName.ENGLISH,
provider_ref_id="CA1234567890abcdef1234567890abcdef",
)
tool_instance = OnStart()
result = await tool_instance.run(context)
print(f"Initial bot message: {result.initial_bot_message}")
print(f"Initial state: {result.initial_state_name}")
print(f"Agent variables: {result.agent_variables}")
asyncio.run(test_on_start())
Example: OnEnd Tool
async def test_on_end():
context = SarvamOnEndToolContext(
user_identifier="user123",
agent_variables={"feedback": "I had a bad experience"},
engagement_metadata=EngagementMetadata(
interaction_id="123",
attempt_id="456",
campaign_id="789",
interaction_language=SarvamToolLanguageName.ENGLISH,
app_id="101",
app_version=1,
agent_phone_number="+1234567890",
),
interaction_transcript=SarvamInteractionTranscript(
interaction_transcript=[
SarvamInteractionTurn(role=SarvamInteractionTurnRole.AGENT, en_text='Hello! How can I help you today?'),
SarvamInteractionTurn(role=SarvamInteractionTurnRole.USER, en_text='I need to book a flight'),
],
interaction_start_time=datetime.now() - timedelta(minutes=2),
interaction_end_time=datetime.now(),
),
provider_ref_id="CA1234567890abcdef1234567890abcdef",
)
tool_instance = OnEnd()
result = await tool_instance.run(context)
print(f"Agent variables: {result.agent_variables}")
print(f"Interaction Retry: {result.retry_interaction}")
asyncio.run(test_on_end())
Best Practices
- Always implement
run()— The entry point for tool execution logic - Use
Field()for parameters — Ensures type safety and LLM prompt metadata - Gracefully handle errors — Avoid accessing unset variables or invalid types
- Return appropriate types —
SarvamTool.run()returnsSarvamToolOutput;OnStartandOnEndreturn their context objects - Write meaningful docstrings — Clearly describe tool purpose for LLM tool-calling
- Use async operations — Use
async/awaitfor I/O to avoid blocking - Use context methods — Prefer provided context methods over direct attribute access