add tags
Trip-level tags: admins configure available tags in the trip header (inline add/remove pills). The AI can also create new tags via the add_tag tool. Event tags: selectable in the Add Event modal, shown as colored pills on event cards in the timeline, and on AI suggestion cards. AI integration: sees available tags in its system prompt, applies them when proposing events, can create new ones with add_tag. Discord: tags shown as inline code blocks under each event in /trip view. Colors: auto-assigned from an 8-color palette by tag index, consistent everywhere.
This commit is contained in:
@@ -47,6 +47,26 @@ _TOOLS = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "add_tag",
|
||||
"description": (
|
||||
"Add a new tag to the trip's available tag list so it can be used on events. "
|
||||
"Use this when you want to apply a tag that doesn't exist yet."
|
||||
),
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"description": "Short tag label, e.g. 'must-do', 'nightlife', 'food'",
|
||||
},
|
||||
},
|
||||
"required": ["tag"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
@@ -66,6 +86,7 @@ _TOOLS = [
|
||||
"location": {"type": "string", "description": "Full address or place name"},
|
||||
"maps_link": {"type": "string", "description": "Google Maps URL"},
|
||||
"notes": {"type": "string", "description": "Brief tips or reasoning"},
|
||||
"tags": {"type": "array", "items": {"type": "string"}, "description": "Tags to apply — must be from the trip's available tags list"},
|
||||
},
|
||||
"required": ["title"],
|
||||
},
|
||||
@@ -133,13 +154,15 @@ def _build_system_prompt(trip: dict, events: list[dict]) -> str:
|
||||
|
||||
itinerary = "".join(lines) if lines else "\n (no events yet)"
|
||||
attendees = ", ".join(trip.get("attendees", {}).values()) or "not specified"
|
||||
available_tags = trip.get("available_tags") or []
|
||||
tags_section = f"\nAvailable tags: {', '.join(available_tags)}" if available_tags else ""
|
||||
|
||||
return f"""You are a trip planning assistant for the following trip.
|
||||
|
||||
Trip: {trip["name"]}
|
||||
Destination: {trip["location"]}
|
||||
Dates: {trip["start_date"]} to {trip["end_date"]}
|
||||
Attendees: {attendees}
|
||||
Attendees: {attendees}{tags_section}
|
||||
|
||||
Current itinerary:{itinerary}
|
||||
|
||||
@@ -148,6 +171,7 @@ Guidelines:
|
||||
- Format all responses using Markdown: use **bold** for place names and key details, bullet lists for options, and [links](url) for Maps links.
|
||||
- When the user mentions places, activities, or asks for suggestions, search for them with search_places before proposing.
|
||||
- Use propose_event for each concrete suggestion — one call per event. The user will approve or skip each one.
|
||||
- When proposing events, apply relevant tags from the available tags list if any are defined.
|
||||
- Be mindful of the existing schedule when assigning times. Avoid obvious conflicts.
|
||||
- All proposed dates must fall between {trip["start_date"]} and {trip["end_date"]}.
|
||||
- If the user says something like "everyone should be there by 6", factor that into your time proposals.
|
||||
@@ -183,6 +207,7 @@ async def create_trip(body: TripCreate):
|
||||
"start_date": body.start_date,
|
||||
"end_date": body.end_date,
|
||||
"attendees": {}, # {discord_user_id: discord_username}
|
||||
"available_tags": body.available_tags,
|
||||
"created_at": now,
|
||||
}
|
||||
await fstore.doc_set("trips", trip_id, doc, merge=False)
|
||||
@@ -199,6 +224,17 @@ async def get_trip(trip_id: str):
|
||||
return {**trip, "events": events}
|
||||
|
||||
|
||||
@router.put("/{trip_id}/tags")
|
||||
async def update_trip_tags(trip_id: str, body: dict):
|
||||
"""Replace the trip's available tag list."""
|
||||
trip = await fstore.doc_get("trips", trip_id)
|
||||
if not trip:
|
||||
raise HTTPException(404, f"Trip '{trip_id}' not found.")
|
||||
tags = [str(t) for t in body.get("available_tags", []) if t]
|
||||
await fstore.doc_update("trips", trip_id, {"available_tags": tags})
|
||||
return {"available_tags": tags}
|
||||
|
||||
|
||||
@router.delete("/{trip_id}")
|
||||
async def delete_trip(trip_id: str, _: dict = Depends(require_service_key_or_admin)):
|
||||
trip = await fstore.doc_get("trips", trip_id)
|
||||
@@ -275,6 +311,7 @@ async def create_event(trip_id: str, body: TripEventCreate):
|
||||
"maps_link": body.maps_link,
|
||||
"place_id": body.place_id,
|
||||
"notes": body.notes,
|
||||
"tags": body.tags,
|
||||
"attendees": {},
|
||||
"created_at": now,
|
||||
}
|
||||
@@ -403,7 +440,19 @@ async def trip_chat(
|
||||
for tc in msg.tool_calls:
|
||||
args = json.loads(tc.function.arguments)
|
||||
|
||||
if tc.function.name == "search_places":
|
||||
if tc.function.name == "add_tag":
|
||||
new_tag = str(args.get("tag", "")).strip()[:50]
|
||||
if new_tag and new_tag not in trip.get("available_tags", []):
|
||||
updated_tags = list(trip.get("available_tags") or []) + [new_tag]
|
||||
trip["available_tags"] = updated_tags
|
||||
await fstore.doc_update("trips", trip_id, {"available_tags": updated_tags})
|
||||
messages.append({
|
||||
"role": "tool",
|
||||
"tool_call_id": tc.id,
|
||||
"content": json.dumps({"available_tags": trip.get("available_tags", [])}),
|
||||
})
|
||||
|
||||
elif tc.function.name == "search_places":
|
||||
# Limit query string lengths before hitting the Maps API
|
||||
query = str(args.get("query", ""))[:200]
|
||||
near = str(args.get("near", ""))[:200]
|
||||
@@ -416,8 +465,10 @@ async def trip_chat(
|
||||
|
||||
elif tc.function.name == "propose_event":
|
||||
suggestion = {k: args.get(k) for k in (
|
||||
"title", "date", "start_time", "end_time", "location", "maps_link", "notes"
|
||||
"title", "date", "start_time", "end_time", "location", "maps_link", "notes", "tags"
|
||||
)}
|
||||
if not isinstance(suggestion.get("tags"), list):
|
||||
suggestion["tags"] = []
|
||||
suggestions.append(suggestion)
|
||||
messages.append({
|
||||
"role": "tool",
|
||||
|
||||
Reference in New Issue
Block a user