Adding Tools to Your Application¶
Time: 20 minutes
Prerequisites: Tutorial 3: Working with Types and Structured Data
Example: 04_tools_and_function_calling.qtype.yaml
What you'll learn:
- Import pre-built tools from the commons library
- Use InvokeTool to call Python functions
- Chain multiple tools with input/output bindings
- Understand tool references and automatic generation
What you'll build: A deadline calculator that uses tools to get the current time, add days, and format the result.
Background: What Are Tools?¶
Tools extend your QType applications with custom steps. Tools are how you plug QType into other existing systems that may not be supported in the shipped interpreter.
A tool is simply a reference to: - A Python function - Call any Python function with typed parameters - An API endpoint - Make HTTP requests to external services
Tools define their inputs (parameters) and outputs (return values) with explicit types.
The Commons Library¶
QType provides a commons library published on GitHub with pre-built tools for common operations:
- Time utilities - Get current time, add/subtract durations, format timestamps
- String operations - Base64 encoding/decoding, text transformations
- Data processing - JSON parsing, field extraction, type conversions
Automatic Tool Generation¶
You don't need to write tool YAML files manually! QType can generate them automatically using qtype convert:
From Python modules:
From OpenAPI specifications:
The converter analyzes function signatures or API schemas and creates properly typed tool definitions.
Part 1: Import and Use Your First Tool (7 minutes)¶
Create Your Application File¶
Create 04_tools_and_function_calling.qtype.yaml:
id: deadline_calculator
description: |
Calculates a deadline by adding days to the current timestamp.
Demonstrates tool imports, InvokeTool step, and tool chaining.
Import the Commons Library¶
Add a references: section before flows::
# Import pre-built tools from the commons library
references:
- !include https://raw.githubusercontent.com/bazaarvoice/qtype/refs/tags/v0.1.11/common/tools.qtype.yaml
What this means:
!include - YAML directive to load another file's content. !include brings in yaml files inside the header.
- Can use local paths: !include ../../common/tools.qtype.yaml
- Or remote URLs: !include https://... (shown here)
- Imports all tools, types, and definitions from that file
references: section - Where you import external components. References can be other applications or lists of models, tools, authorization providers, variables, or custom types.
You can now reference any tool by its id (like qtype.application.commons.tools.get_current_timestamp).
Check your work:
Should pass ✅ (even with no flows yet - imports are valid)
Define Your Flow Variables¶
Add the flow structure:
flows:
- id: calculate_deadline
description: Calculate a formatted deadline from current time plus days
inputs:
- days_until_due
outputs:
- deadline_formatted
variables:
# Input
- id: days_until_due
type: int
# Tool outputs
- id: current_time
type: datetime
- id: deadline_time
type: datetime
- id: format_string
type: text
- id: deadline_formatted
type: text
New types:
datetime type - Built-in QType type for timestamps:
- Represents a point in time (date + time)
- Stored internally as ISO 8601 strings
- Automatically converted to/from Python datetime objects
- Tools can accept and return datetime values
Why all these variables? Each tool transforms data:
- current_time ← output from get_current_timestamp
- deadline_time ← output from timedelta (current_time + days)
- deadline_formatted ← output from format_datetime (pretty string)
Explicit variables make the data flow visible and debuggable.
Add Your First Tool Call¶
Add this under steps::
steps:
# Step 1: Get current timestamp using a tool
# This tool takes no inputs and returns the current UTC time
- id: get_current_time
type: InvokeTool
tool: qtype.application.commons.tools.get_current_timestamp
input_bindings: {}
output_bindings:
result: current_time
outputs:
- current_time
New step type: InvokeTool
This is your primary way to call tools in QType flows.
tool: - Full ID of the tool to invoke:
- Format: <module_id>.<function_name>
- Must match a tool defined in your imports or application
- Example: qtype.application.commons.tools.get_current_timestamp
input_bindings: - Maps flow variables to tool parameters:
- Empty {} means no inputs needed
- This tool has no parameters - it just returns the current time
output_bindings: - Maps tool outputs to flow variables:
- result: current_time means "take the tool's result output and store it in the current_time variable"
- Tool outputs are defined in the tool's YAML definition
outputs: - Flow-level outputs this step produces:
- Lists which flow variables this step creates or modifies
- Used by QType to validate data flow through the pipeline
Check your work:
Should pass ✅
Part 2: Chain Tools with Bindings (8 minutes)¶
Add a Constant Variable¶
Before we can format our datetime, we need to define the format string:
# Step 2: Create a format string constant
- id: create_format_string
type: PromptTemplate
template: "%B %d, %Y at %I:%M %p UTC"
inputs: []
outputs:
- format_string
Pattern: Constants in flows
Since tool input_bindings only accept variable names (not literal values), we use PromptTemplate to create constants:
- Template with no placeholders → constant string
- inputs: [] → no dependencies
- Produces format_string variable for later use
Format string syntax: Uses Python's strftime format codes:
- %B - Full month name (January)
- %d - Day of month (14)
- %Y - 4-digit year (2026)
- %I:%M %p - Time in 12-hour format (03:30 PM)
Add Days with Input Bindings¶
Now let's use a tool with multiple inputs:
# Step 3: Calculate deadline by adding days to current time
# input_bindings maps flow variables to tool parameters
- id: add_days
type: InvokeTool
tool: qtype.application.commons.tools.timedelta
input_bindings:
timestamp: current_time
days: days_until_due
output_bindings:
result: deadline_time
outputs:
- deadline_time
Understanding bindings:
Input bindings structure:
In this case:
- Tool parameter timestamp ← gets value from flow variable current_time
- Tool parameter days ← gets value from flow variable days_until_due
The timedelta tool definition (from commons library) looks like:
inputs:
timestamp:
type: datetime
days:
type: int
hours:
type: int
optional: true
# ... more optional parameters
Optional parameters: You only need to bind the required parameters. timedelta has many optional parameters (hours, minutes, seconds, weeks), but we only use days.
Chain Tools Together¶
Finally, format the deadline using the output from the previous step:
# Step 4: Format deadline for human readability
# Shows chaining: output from previous tool becomes input to this one
- id: format_deadline
type: InvokeTool
tool: qtype.application.commons.tools.format_datetime
input_bindings:
timestamp: deadline_time
format_string: format_string
output_bindings:
result: deadline_formatted
outputs:
- deadline_formatted
Check your work:
Should pass ✅
Part 3: Test Your Tools (5 minutes)¶
Run the Application¶
Expected output:
The exact time will match when you run it, but the date should be 3 days from now.
Try Different Durations¶
# One week deadline
qtype run -i '{"days_until_due": 7}' 04_tools_and_function_calling.qtype.yaml
# Two weeks
qtype run -i '{"days_until_due": 14}' 04_tools_and_function_calling.qtype.yaml
# Same day (0 days)
qtype run -i '{"days_until_due": 0}' 04_tools_and_function_calling.qtype.yaml
Add the --progress Flag¶
For more visibility into tool execution:
You'll see each step execute in real-time:
Step get_current_time ✔ 1 succeeded
Step create_format_string ✔ 1 succeeded
Step add_days ✔ 1 succeeded
Step format_deadline ✔ 1 succeeded
Part 4: Understanding Tools Deeply (Bonus)¶
Tool Types and Custom Types¶
Remember from Tutorial 3 where calculate_time_difference returns TimeDifferenceResultType? That's a custom type defined in the commons library:
types:
- id: TimeDifferenceResultType
properties:
days: int
seconds: int
microseconds: int
total_hours: float
total_minutes: float
total_seconds: float
total_days: float
If you use calculate_time_difference, you can extract fields from its result:
- id: calc_difference
type: InvokeTool
tool: qtype.application.commons.tools.calculate_time_difference
input_bindings:
start_time: start
end_time: end
output_bindings:
result: time_diff # time_diff is now TimeDifferenceResultType
# Later, access fields using FieldExtractor or Construct
Generating Your Own Tools¶
When you're ready to create custom tools, use qtype convert:
From a Python module:
# Generate tools from all functions in myapp.utils
qtype convert python --module myapp.utils --output my_tools.qtype.yaml
QType will: - Scan all public functions in the module - Extract type hints from function signatures - Generate tool definitions with proper input/output types - Include docstrings as descriptions
From an OpenAPI spec:
# Generate tools from a REST API
qtype convert openapi --spec weather_api.yaml --output weather_tools.qtype.yaml
QType will: - Parse endpoint definitions - Create a tool for each operation - Map request parameters to tool inputs - Map response schemas to tool outputs - Handle authentication configurations
What You've Learned¶
Congratulations! You've mastered:
✅ Tool concepts - References to Python functions or API calls
✅ Importing tools - Using !include with local or remote files
✅ InvokeTool step - Calling tools with input/output bindings
✅ Input bindings - Mapping flow variables to tool parameters
✅ Output bindings - Mapping tool results to flow variables
✅ Tool chaining - Connecting outputs to inputs across steps
✅ Commons library - Pre-built tools for common operations
✅ Tool generation - Using qtype convert to create tool definitions
Next Steps¶
Reference the complete example:
04_tools_and_function_calling.qtype.yaml- Full working example- Commons Library - Browse all available tools
- Commons Library Source - Browse the source of tools.
Common Questions¶
Q: Can I call multiple tools in parallel?
A: Not directly in a single step. However, if tools don't depend on each other's outputs, QType's execution engine may parallelize them automatically based on dependency analysis.
Q: What happens if a tool raises an error?
A: The flow stops and returns an error.
Q: Can I pass literal values instead of variables to tools?
A: No - input_bindings only accepts variable names. Use PromptTemplate to create constant variables, or define them in your flow's input data.
Q: How do I know what parameters a tool accepts?
A: Check the tool's YAML definition (in the commons library or your generated file). It lists all inputs with their types and whether they're optional.
Q: Can tools modify variables or have side effects?
A: Tools are functional - they take inputs and return outputs without modifying flow state. Side effects (like writing files or calling APIs) happen inside the tool implementation, but they don't affect other flow variables.
Q: What's the difference between InvokeTool and Agent?
A: InvokeTool explicitly calls a specific tool with defined bindings. Agent gives an LLM access to multiple tools and lets it decide which to use and when. Use InvokeTool for deterministic workflows, Agent for autonomous decision-making.