Files
README-first/progress/tech/PROJECT-giving-event-matching.md

18 KiB

Matching Common Interests at an Event

We currently have an onboarding meeting tool as described in the onboarding doc.

We want to enhance this process such that the meeting can be an event where attendees share their interests and get matched with others of similar interests for potential future collaboration .

  • People without a profile are prompted to create a profile.

  • The organizer can trigger a round of pairing, where the system looks at the profiles and attempts to match people based on their similarities.

    • The matching is best as an AI semantic match.

    • Each pair is given a number.

    • The organizer can put people into groups who should NOT be paired together.

    • If there is an odd number of people, the organizer can assign themselves or anyone else to be a non-participant and excluded from the matching.

    • The app should show the participants

    • The organizer can trigger another round, where the people are all paired with different people. This can happen any number of times, until there are no more different matches that can be made.

  • The attendees can see a list of the other attendees in the future, even when the meeting is deleted.

Implementation

Phase 0: Vector Similarity Foundation (Test-Driven)

Goal: Prove vector similarity matching works effectively via unit tests

Note: Build and validate core matching algorithms before UI integration

  • Implement embedding generation function
    • generateEmbedding(text) - call OpenAI API to generate embeddings
    • Handle API keys via environment variables
    • Error handling for API failures
    • Unit tests with real profile descriptions
  • Implement pure JavaScript vector math functions
    • dotProduct(vec1, vec2) - multiply and sum vector components
    • magnitude(vec) - calculate vector length
    • cosineSimilarity(vec1, vec2) - measure similarity (0-1 scale)
    • Unit tests for each function with known inputs/outputs
  • Create test profiles with embeddings
    • Profile 1: Sustainable agriculture focus
    • Profile 2: Similar to Profile 1 (should match highly)
    • Profile 3: Software/tech focus (different from 1 & 2)
    • Profile 4: Community organizing (partial overlap with all)
    • Generate real embeddings from descriptions using OpenAI API
    • Verify embeddings are 1536-dimensional vectors
  • Test similarity calculations
    • Verify high similarity (>0.8) between similar profiles
    • Verify low similarity (<0.5) between dissimilar profiles
    • Verify medium similarity for partial overlaps
    • Test with actual embedding vectors (1536 dimensions)
  • Test basic pairing algorithm
    • 4 people → 2 pairs (highest similarities)
    • 6 people → 3 pairs
    • 5 people → 2 pairs + 1 trio (group of 3) Note: Changed to require even numbers
    • Verify pairs have higher similarity than non-pairs
  • Test constraint handling
    • Exclude specific pairs from matching
    • Exclude individuals from matching pool
    • Multiple rounds with no repeated pairs
  • Test edge cases
    • 2 people (minimum viable)
    • 3 people (one trio) Note: Changed to throw error for odd numbers
    • All identical profiles (any pairing is equally good)
    • Empty/minimal profile handling
  • Automated Testing

Validation: All tests pass showing effective profile matching based on semantic similarity

Test File: repos/endorser-ch/test/controller-partner-3-group-matching.js

Implementation Summary

Files Created:

  • repos/endorser-ch/test/controller-partner-3-group-matching.js (783 lines, 60+ tests)
  • repos/endorser-ch/test/generate-test-embeddings.js (helper script for generating real embeddings)
  • Added generate-test-embeddings npm script to package.json
  • Updated test/README.md with usage documentation
  • Embeddings cached in embeddings.json with metadata (model, provider, dimensions)

Core Functions Implemented:

  1. generateEmbedding(text, apiKey) - Calls OpenAI API to generate 1536-dimensional embeddings

    • Uses text-embedding-3-small model
    • Error handling for API failures
    • Environment variable support for API key
  2. dotProduct(vec1, vec2) - Multiplies and sums vector components

  3. magnitude(vec) - Calculates vector length

  4. cosineSimilarity(vec1, vec2) - Returns similarity score from -1 to 1

  5. matchParticipants(participants, excludedPairs, excludedIds, previousPairs)

    • Greedy pairing algorithm based on similarity scores
    • Handles odd numbers by creating trios
    • Supports exclusion constraints and multiple rounds
    • Returns structured pair/trio objects with similarity scores

Test Coverage:

  • Embedding generation (5 tests) - validates OpenAI API integration
  • Vector math (9 tests) - validates dot product, magnitude, cosine similarity
  • Profile similarity (4 tests) - confirms high similarity for similar profiles (>0.95), low for dissimilar (<0.6)
  • Pairing algorithm (6 tests) - validates 2-6 person groups, trio handling
  • Constraint handling (3 tests) - validates exclusions and multiple rounds
  • Edge cases (5 tests) - minimum groups, identical profiles, error handling
  • Match quality (2 tests) - validates within-pair > cross-pair similarity

Test Profiles: 26 diverse profiles with descriptions ranging from sustainable agriculture to software development, designed to test various similarity scenarios. Includes profiles focused on education, construction, AI/ML, firearms, outdoors, mushroom cultivation, travel, and sports. Length varies from 3 words to full paragraphs to test that matching works regardless of description length.

Performance:

  • Vector similarity calculation: <1ms per pair
  • 20 participants (190 comparisons): ~5-10ms
  • Embedding generation: ~100-200ms per API call, $0.00002 cost per profile

Usage:

# Quick testing with simplified embeddings
npm test test/controller-partner-3-group-matching.js

# Generate real 1536-dimensional embeddings (one-time)
export OPENAI_API_KEY=your-key-here
npm run test:generate-embeddings

# Tests automatically use real embeddings if available

Status: Complete - All algorithms validated and ready for API integration in Phase 1


Phase 1: Basic Profile Integration (Proof of Concept)

Goal: Integrate existing endorser-ch profile system with meeting feature

Note: Profile storage already exists in endorser-ch partner-api as a simple text field

Phase 1.1:

  • Allow users to create labels to attach to their contacts
    • The labels can be created on the edit-user screen
    • Show the labels on each user on the DID view contact-details screen
    • Show the labels at the top of the contacts page, and only show those contacts if the users clicks that label

Phase 1.2:

  • Create flag for permissioned profiles
    • Allow a back-end (endorser-ch) flag to tag individuals as always generating embedding vectors
    • Add an admin user array, set via environment variable or .env file
    • Add tests that only allow an admin to set the generateEmbeddings flag. Allow it for a user who has a profile and also for for a user that does not.
    • For any admin user, put a button on the DID view contact screen on the front-end (crowd-funder-pwa) that allows the admin user to tag the user DID to always generate embedding vectors

Phase 1.3:

  • Verify existing profile field supports matching use case
    • Confirm profile text field can hold interests, skills, and goals together
    • Check field length limits are adequate for detailed descriptions
  • Profile prompt on meeting join
    • Check if attendee has profile
    • Show profile creation/update modal if needed, but allow them to cancel
  • Image prompt on meeting join
    • Show image creation/update modal if needed, but allow them to cancel

Phase 1.4:

  • Display profiles to other attendees
    • In the list of attendees, link to profile for each user if it's known
  • Automated Testing

Validation: Attendees can see all attendee profiles after they join


Phase 2: AI-Powered Profile Matching

Goal: Implement core semantic matching algorithm

  • Set up AI/LLM integration

    • Use OpenAI with the text-embedding-3-small model
    • Configure API keys and rate limits
    • Create embeddings service for profile text
    • Store in a new partner table named profile_embedding with the profile ID and with the embeddings as a comma-separated vector string
    • For anyone with the generateEmbeddings flag on their profile: call to OpenAI to get their embedding vector when the flag is set, and also when a profile with that flag is changed.
    • Allow blank value for embedding and hard-code their vector as the "data.empty.embedding" value from the test/embedding-empty-string.json file
    • Allow organizers with generateEmbeddings flag to select from their list of contacts, and then hit a button to "Check for Profile" that takes them to a screen which will check each of those contacts for a valid profile, one-by-one, and give a report. Each contact should have a link to the DID view so they can see that person's contact info.
      • Display on that page the "embedding metadata" much like on the DID view page.
  • Implement matching algorithm

    • Meeting organizer can trigger a pairing session
    • Allow attendees to be excluded (ban icon toggle per member; persisted in localStorage scoped to meeting)
    • Pairing data is saved in the DB
    • Calculate similarity scores between all pairs
    • Create pairing algorithm (maximize total similarity)
    • Create "do not pair" groups (named, color-coded groups; members within a group are never paired)
    • Pre-match confirmation dialog showing included members, excluded members, do-not-pair groups, and previous pair count
    • "Not Paired" summary after matching shows who was left out and why (individually excluded, do-not-pair group, or odd number)
  • Basic matching endpoint

    • POST endpoint for organizer to trigger matching
    • Return list of pairs with similarity scores
  • Simple results display

    • Show matched pairs to attendees
    • Assign numbers to each pair
  • Automated Testing

Validation: Organizer can trigger matching, and all attendees can see AI-generated pairs with reasonable similarity

Exclusion & Do-Not-Pair Implementation Summary (Phase 2 + Phase 4 overlap)

Backend (endorser-ch): No changes needed. matchParticipants() in matching.service.js already accepts excludedDids, excludedPairDids, and previousPairDids. The POST /api/partner/groupOnboardMatch endpoint passes these through.

Frontend (crowd-funder-for-time-pwa) files changed:

  • src/interfaces/user.ts — Added DoNotPairGroup and MeetingExclusionState interfaces
  • src/components/MeetingExclusionGroups.vue (new, ~240 lines) — Standalone component for managing do-not-pair groups. Color-coded group cards with member chips, add/remove controls, and a disabled prop for read-only mode when matches exist.
  • src/components/MeetingMembersList.vue — Added excludedDids / exclusionLocked props, ban icon toggle per admitted member, getAdmittedMembers() method exposed for parent, members-loaded emit for parent sync.
  • src/views/OnboardMeetingSetupView.vue — Orchestration: exclusion state management with localStorage persistence (scoped to meeting), pre-match confirmation dialog, "Not Paired" summary after matching, locking when matches exist, expanded postNewMatchesThenRefresh() body with all three constraint fields.

Key design decisions:

  • Exclusion state persists in localStorage under key meeting-exclusion-state, scoped by meetingGroupId. Automatically clears when the organizer switches meetings.
  • Locking: all exclusion controls become read-only when hasActiveMatches is true. "Erase to Start Over" unlocks them.
  • admittedMembers is a reactive data property (not a computed via $refs) populated via the members-loaded event from MeetingMembersList, avoiding $refs timing issues.

Phase 3: Matching UI and Participant Experience

Goal: Polish the matching visualization and participant-facing features

  • Enhance organizer matching interface
    • Visual display of pairs (cards, grid, or list)
    • Show pair numbers prominently
    • Create a new screen that lists all the attendees, along with their profile (if it's visible to the organizer) -- truncated, but allowed to be expanded
  • Participant view of matches
    • Show participants their assigned pair
    • Display partner's profile information
    • Show pair number
  • Real-time updates
    • WebSocket or polling for match announcements
    • Notify participants when matching is triggered
  • Allow easy adding of notes onto the contact of the matched person
  • Allow easy adding of an offer to the matched person
  • Automated Testing

Validation: Both organizer and participants see clear, real-time matching results


Phase 4: Multiple Rounds and Constraints

Goal: Support advanced matching scenarios

  • Exclusion groups ("Do Not Pair")
    • UI for organizer to create named, color-coded groups and assign members
    • Persist exclusion rules in localStorage scoped to meeting (auto-clears on meeting change)
    • Groups expand to all DID pairs and are sent as excludedPairDids to the server
    • Controls are locked (read-only) while matches exist; unlock on "Erase to Start Over"
  • Exclude non-participants
    • Ban icon toggle per admitted member to exclude from matching pool
    • Excluded members shown with strikethrough and amber styling
    • Ban icon always visible; shows notification explaining lock state when matches exist
    • Excluded DIDs sent as excludedDids in POST body
  • "Erase to Start Over" always clickable; shows notification when no matches exist or matching is in progress
  • Multiple matching rounds
    • Track previous pairs in meeting state
    • Constraint: don't repeat previous pairs
    • Calculate when no more unique pairs possible
    • Show organizer "rounds remaining" indicator
  • Round history
    • Store all previous rounds
    • Allow organizer to view past pairings
    • Display round number to participants
  • Automated Testing

Validation: Organizer can run multiple rounds with no repeated pairs and proper exclusions


Phase 5: Post-Event Features and Polish

Goal: Enable long-term value and edge case handling

  • Allow a back-end (endorser-ch) flag to mark users with admin permission, so they can tag individuals as always generating embedding vectors
  • Post-event attendee list
    • Create "past event" view showing all attendees
    • Link to profiles even after event expires
  • Meeting expiration handling
    • Archive meeting data on client side
  • Analytics and insights
    • Show match quality scores to organizer
    • Track which pairs connected post-event
    • Export attendee list and matching history
  • Profile enhancements
    • Add optional profile photo
    • Rich text formatting for longer descriptions
    • Skills taxonomy or tags
  • Error handling and edge cases
    • Handle API failures gracefully
    • Timeout handling for long matching operations
    • Support for very small (2-3 people) or large (50+) groups
    • Handle profile updates mid-matching
  • Performance optimization
    • Cache embeddings to avoid regeneration
    • Optimize matching algorithm for large groups
    • Add loading states and progress indicators
  • Automated Testing

Validation: System handles all edge cases gracefully and provides long-term value post-event


Phase 6: Social Media Integration (Optional)

Goal: Auto-populate interests from social media profiles

Note: This is an optional enhancement that could significantly improve onboarding UX

  • OAuth integration setup
    • Facebook OAuth flow
    • LinkedIn OAuth flow (professional interests/skills)
    • Twitter/X OAuth flow (interests from bio/tweets)
    • Secure token storage and management
  • Data extraction and parsing
    • Facebook: Extract liked pages, groups, interests from profile
    • LinkedIn: Extract skills, interests, job descriptions
    • Twitter/X: Parse bio, analyze recent tweets for topics
    • Create unified interest extraction format
  • AI-powered profile summarization
    • Feed social media data to LLM
    • Generate concise profile text combining interests, skills, and goals
    • Allow user to review and edit before saving
  • Profile enrichment UI
    • "Import from social media" button on profile form
    • Platform selection interface
    • Preview extracted data before applying
    • Merge with existing profile data (don't overwrite)
  • Privacy and consent
    • Clear consent flow explaining data usage
    • Option to delete imported data
    • Don't store raw social media data (only processed interests)
    • Allow users to see what data was extracted
  • Multiple platform support
    • Allow importing from multiple platforms
    • Intelligently merge profile data from different sources into single text
    • Deduplicate similar information from multiple platforms

Validation: Users can import interests from social media, review them, and have auto-populated profiles

Benefits:

  • Dramatically reduces friction for new users
  • More comprehensive profiles with richer context
  • Better matching quality with more detailed descriptions
  • Engages users who might skip manual profile creation

Privacy Considerations:

  • Only request minimal scopes from OAuth providers
  • Process and discard raw data immediately
  • Store only the generated profile text summary
  • Comply with platform APIs terms of service
  • Provide clear data deletion options

Technical Considerations

AI/LLM Integration:

  • Use sentence transformers or embedding models for semantic similarity
  • Generate embeddings from user's free-form profile text
  • Caching strategy for embeddings to reduce API costs

Matching Algorithm:

  • Start with greedy pairing (highest similarity pairs first)
  • Could evolve to weighted bipartite matching for optimal global solution
  • Need to handle constraints efficiently (exclusions, previous pairs)