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"