Python API
This page shows how to run tour meetings programmatically from Python scripts, through a few common use cases.
Note
We plan to distribute the Python API as a PyPI package in the future.
Run a meeting
The core entry point is build_meeting, which assembles an AITourMeeting from participant config dicts — the same personas, constraints, and workflow settings you would set in the GUI. Calling run_cli() runs the whole meeting and prints the conversation, proposals, and voting to stdout.
import asyncio
from tour_meeting.cli import build_meeting
meeting = build_meeting(
title="One-Day Tokyo Tour",
global_goals="Plan a fun one-day walking tour in Tokyo.",
participants=[
{
"name": "Alice",
"background": "A history enthusiast visiting Tokyo for the first time.",
"personality": "Curious and detail-oriented.",
"preferences": "Prefers temples and quiet historical sites over crowds.",
"personal_goals": "Visit Senso-ji and the Imperial Palace.",
"model_name": "vllm/0/Qwen/Qwen3-8B",
"role": "facilitator",
},
{
"name": "Bob",
"background": "A food blogger who writes about street food.",
"personality": "Enthusiastic and spontaneous.",
"preferences": "Wants to try local street food over sit-down restaurants.",
"personal_goals": "Explore Tsukiji Outer Market and ramen shops.",
"model_name": "vllm/0/Qwen/Qwen3-8B",
"system_prompt": "You are {name}, an enthusiastic foodie. {background}\nFocus on: {personal_goals} ...",
},
],
constraints={"budget": "$100", "time_window_start": "09:00", "time_window_end": "18:00"},
settings={"max_turns": 100, "turn_rule": "round_robin", "voting_rule": "majority"},
)
asyncio.run(meeting.run_cli())
Stream meeting events
When you want to process the meeting yourself — log it in your own format, feed it into another system, or stop early on some condition — use run_free_conversation() instead of run_cli(). It yields typed events (TurnFinal, ProposalVoteResult, MeetingFinished, and more) as the meeting progresses.
import asyncio
from tour_meeting.cli import build_meeting
from tour_meeting.types import MeetingFinished, ProposalVoteResult, TurnFinal
meeting = build_meeting(...) # same as above
async def main():
async for event in meeting.run_free_conversation():
if isinstance(event, TurnFinal):
print(f"[turn {event.turn}] {event.speaker}: {event.text[:80]}")
elif isinstance(event, ProposalVoteResult):
verdict = "accepted" if event.accepted else "rejected"
print(f"[vote] {event.proposer}'s proposal was {verdict}")
elif isinstance(event, MeetingFinished):
print(f"[done] finished after {event.turns} turns")
asyncio.run(main())
Export analytics
After a meeting finishes, export_analytics() returns all raw analytics data as a dictionary — discussion dynamics, route snapshots and transitions, and metadata — the same data behind the GUI's analytics dashboard. Save it as JSON and analyze it with your favorite tools.
import asyncio
import json
from tour_meeting.cli import build_meeting
meeting = build_meeting(...) # same as above
asyncio.run(meeting.run_cli())
analytics = meeting.export_analytics()
with open("tokyo_tour_analytics.json", "w", encoding="utf-8") as f:
json.dump(analytics, f, indent=2, ensure_ascii=False)
# e.g. inspect the final adopted route
final_route = analytics["route_characteristics"]["route_snapshots"][-1]
for d in final_route["destinations"]:
print(d["name"])
Compare meeting workflows
Because a meeting is just a Python object, you can sweep over workflow settings for experiments — for example, running the same scenario under different voting rules and collecting the analytics of each run.
import asyncio
import json
from tour_meeting.cli import build_meeting
participants = [...] # same as above
results = {}
for voting_rule in ["majority", "unanimous", "most_pleasure", "least_misery"]:
meeting = build_meeting(
title="One-Day Tokyo Tour",
global_goals="Plan a fun one-day walking tour in Tokyo.",
participants=participants,
settings={"max_turns": 100, "voting_rule": voting_rule},
)
asyncio.run(meeting.run_cli())
results[voting_rule] = meeting.export_analytics()
with open("voting_rule_sweep.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
Running scripts
Scripts are executed inside the backend Docker container, so make up must be running first.
# Start containers
make up
# Run a script
make run SCRIPT=path/to/your_tour.py
# Run a script with arguments
make run SCRIPT=path/to/your_tour.py ARGS="--model openai/gpt-5.2"