-
Notifications
You must be signed in to change notification settings - Fork 4
agent: @U0AJM7X8FBR implement the wildest craziest most WOW demoable feature. M #328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: test
Are you sure you want to change the base?
Changes from all commits
5b952a0
57cf714
ee55513
a65399a
52ee805
919ea62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { NextRequest, NextResponse } from "next/server"; | ||
| import { getCorsHeaders } from "@/lib/networking/getCorsHeaders"; | ||
| import { generateArtistIntelPackHandler } from "@/lib/artistIntel/generateArtistIntelPackHandler"; | ||
|
|
||
| /** | ||
| * OPTIONS handler for CORS preflight requests. | ||
| * | ||
| * @returns A NextResponse with CORS headers. | ||
| */ | ||
| export async function OPTIONS() { | ||
| return new NextResponse(null, { | ||
| status: 200, | ||
| headers: getCorsHeaders(), | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * POST /api/artists/intel | ||
| * | ||
| * Generates a complete Artist Intelligence Pack for any artist. | ||
| * | ||
| * Combines three data sources in parallel: | ||
| * 1. Spotify — Artist profile (name, genres, followers, popularity) + top tracks with | ||
| * 30-second audio preview URLs. | ||
| * 2. MusicFlamingo (NVIDIA 8B) — AI audio analysis of the artist's top track via the | ||
| * Spotify preview URL: genre/BPM/key/mood (catalog_metadata), target audience | ||
| * demographics (audience_profile), playlist pitch targets (playlist_pitch), and | ||
| * mood/vibe tags (mood_tags). | ||
| * 3. Perplexity — Real-time web research: recent press, streaming news, and trending | ||
| * moments for the artist. | ||
| * | ||
| * An AI marketing strategist then synthesizes all three sources into a ready-to-use | ||
| * marketing pack: playlist pitch email, Instagram/TikTok/Twitter captions, press | ||
| * release opener, and key talking points. | ||
| * | ||
| * Request body: | ||
| * - artist_name (required): The artist name to analyze (e.g. "Taylor Swift", "Bad Bunny"). | ||
| * | ||
| * @param request - The request object containing a JSON body. | ||
| * @returns A NextResponse with the complete intelligence pack (200) or an error. | ||
| */ | ||
| export async function POST(request: NextRequest) { | ||
| return generateArtistIntelPackHandler(request); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import "@/lib/coding-agent/handlers/registerHandlers"; | |
| * Handles webhook verification handshakes (e.g. WhatsApp hub.challenge). | ||
| * | ||
| * @param request - The incoming verification request | ||
| * @param params.params | ||
| * @param params - Route params containing the platform name | ||
| */ | ||
| export async function GET( | ||
|
|
@@ -34,6 +35,7 @@ export async function GET( | |
| * Handles Slack and WhatsApp webhooks via dynamic [platform] segment. | ||
| * | ||
| * @param request - The incoming webhook request | ||
| * @param params.params | ||
| * @param params - Route params containing the platform name | ||
|
Comment on lines
+38
to
39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same JSDoc error repeated for POST handler. 📝 Suggested fix- * `@param` params.params
* `@param` params - Route params containing the platform name
+ * `@param` params.platform - The platform identifier (e.g., "slack", "whatsapp")🤖 Prompt for AI Agents |
||
| */ | ||
| export async function POST( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ import type { User } from "@privy-io/node"; | |
| /** | ||
| * Returns the most recent latest_verified_at (in ms) across all linked_accounts for a Privy user. | ||
| * Returns null if no linked account has a latest_verified_at. | ||
| * | ||
| * @param user | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use account-specific terminology in JSDoc param naming.
As per coding guidelines, "Use 'account' terminology, never 'entity' or 'user'; use specific names like 'artist', 'workspace', 'organization' when referring to specific types". 🤖 Prompt for AI Agents |
||
| */ | ||
| export function getLatestVerifiedAt(user: User): number | null { | ||
| const linkedAccounts = user.linked_accounts; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| import { describe, it, expect } from "vitest"; | ||
| import { computeArtistOpportunityScores } from "@/lib/artistIntel/computeArtistOpportunityScores"; | ||
|
|
||
| describe("computeArtistOpportunityScores", () => { | ||
| describe("pre-market artist (very low followers + low popularity)", () => { | ||
| it("detects a pre-market artist like Gatsby Grace (2 followers, 0 popularity)", () => { | ||
| const scores = computeArtistOpportunityScores( | ||
| null, // no music analysis (no preview URL) | ||
| 2, // followers — real Gatsby Grace Spotify data | ||
| 0, // popularity — real Gatsby Grace Spotify data | ||
| null, // no peer benchmark (Spotify has no related artists for pre-market acts) | ||
| ); | ||
|
|
||
| // All scores should be in the weak-to-moderate range — early stage, not established | ||
| expect(scores.sync.score).toBe(30); // 30 + 0*0.4 = 30 | ||
| expect(scores.sync.rating).toBe("weak"); | ||
| expect(scores.sync.rationale).toContain("Audio analysis unavailable"); | ||
|
|
||
| expect(scores.playlist.score).toBe(35); // 35 + 0*0.5 = 35 | ||
| expect(scores.playlist.rating).toBe("weak"); | ||
|
|
||
| // A&R rationale should say "pre-market" — NOT "established/saturated" | ||
| expect(scores.ar.rationale).toContain("Pre-market artist"); | ||
| expect(scores.ar.rationale).not.toContain("established"); | ||
| expect(scores.ar.rationale).not.toContain("saturated"); | ||
| expect(scores.ar.score).toBeGreaterThan(40); // gets a 10-pt early-discovery bonus | ||
|
|
||
| expect(scores.brand.score).toBe(35); // 35 + 0*0.35 = 35 | ||
| expect(scores.brand.rating).toBe("weak"); | ||
|
|
||
| // Overall should reflect early-stage reality | ||
| expect(scores.overall).toBeGreaterThan(0); | ||
| expect(scores.overall).toBeLessThan(50); | ||
| }); | ||
|
|
||
| it("uses growing/maturing rationale for artist with many followers but low popularity", () => { | ||
| const scores = computeArtistOpportunityScores( | ||
| null, | ||
| 5_000_000, // high followers | ||
| 5, // but very low popularity — not getting algorithm play | ||
| null, | ||
| ); | ||
|
|
||
| // Should NOT trigger pre-market — this is an established artist with low traction | ||
| expect(scores.ar.rationale).not.toContain("Pre-market artist"); | ||
| expect(scores.ar.rationale).toContain("growing or maturing"); | ||
| }); | ||
|
|
||
| it("gives higher efficiency bonus when popularity is high relative to small audience", () => { | ||
| // Artist with 500 followers but 60 popularity = punching way above weight | ||
| const scores = computeArtistOpportunityScores(null, 500, 60, null); | ||
|
|
||
| // followerEfficiency = 60 / log10(501) ≈ 60 / 2.7 ≈ 22.2 → "High" bonus (+25) | ||
| expect(scores.ar.score).toBeGreaterThan(60); | ||
| expect(scores.ar.rationale).toContain("High popularity-to-follower ratio"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("with music analysis", () => { | ||
| const fullMusicAnalysis = { | ||
| catalog_metadata: { | ||
| genre: "indie pop", | ||
| subgenres: ["bedroom pop"], | ||
| tempo_bpm: 110, | ||
| key: "C major", | ||
| time_signature: "4/4", | ||
| energy_level: 6, | ||
| danceability: 7, | ||
| mood: ["uplifting", "nostalgic", "dreamy"], | ||
| instruments: ["guitar", "piano", "synth", "drums"], | ||
| vocal_style: "breathy", | ||
| production_style: "polished, studio-quality", | ||
| similar_artists: ["Clairo", "Phoebe Bridgers"], | ||
| description: "Dreamy indie pop with bedroom vibes", | ||
| }, | ||
| audience_profile: { | ||
| age_range: "18-24", | ||
| gender_skew: "female", | ||
| lifestyle_tags: ["coffee shops", "college campus", "late nights", "aesthetics"], | ||
| listening_contexts: ["studying", "commuting", "late night"], | ||
| platforms: ["Spotify", "Apple Music", "TikTok"], | ||
| comparable_fanbases: ["Clairo fans", "Phoebe Bridgers fans"], | ||
| marketing_hook: "The soundtrack to your 3am thoughts", | ||
| }, | ||
| playlist_pitch: "Perfect for late-night indie playlists and bedroom pop discovery", | ||
| mood_tags: { | ||
| tags: ["dreamy", "nostalgic", "cozy"], | ||
| primary_mood: "contemplative", | ||
| }, | ||
| }; | ||
|
|
||
| it("computes sync score from BPM and energy data", () => { | ||
| const scores = computeArtistOpportunityScores(fullMusicAnalysis, 10_000, 45, null); | ||
|
|
||
| // BPM 110 (in 70-130 range): +10 | ||
| // Energy 6 (in 3-8 range): +10 | ||
| // 3 moods: +8 | ||
| // polished production: +7 | ||
| // 4 instruments: +5 | ||
| // base: 50 | ||
| // total: 90 → capped at 100 → "exceptional" | ||
| expect(scores.sync.score).toBeGreaterThan(60); | ||
| expect(scores.sync.rationale).toContain("BPM"); | ||
| }); | ||
|
|
||
| it("computes playlist score from danceability", () => { | ||
| const scores = computeArtistOpportunityScores(fullMusicAnalysis, 10_000, 45, null); | ||
|
|
||
| // danceability 7: 7*5 = 35 points | ||
| // energy 6: +5 for moderate | ||
| // popularity 45 * 0.15 = +7 | ||
| // base: 40 | ||
| // total: 87 → "exceptional" | ||
| expect(scores.playlist.score).toBeGreaterThan(70); | ||
| expect(scores.playlist.rationale).toContain("Danceability"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("overall score weighting", () => { | ||
| it("computes overall as weighted average of four scores", () => { | ||
| const scores = computeArtistOpportunityScores(null, 100_000, 50, null); | ||
|
|
||
| // expected overall = Math.round(ar*0.3 + playlist*0.25 + sync*0.25 + brand*0.2) | ||
| const expectedOverall = Math.round( | ||
| scores.ar.score * 0.3 + | ||
| scores.playlist.score * 0.25 + | ||
| scores.sync.score * 0.25 + | ||
| scores.brand.score * 0.2, | ||
| ); | ||
|
|
||
| expect(scores.overall).toBe(expectedOverall); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect JSDoc parameter:
@param params.paramsis nonsensical.The parameter structure is
{ params }: { params: Promise<{ platform: string }> }, so the correct documentation should referenceparams.platform, notparams.params.📝 Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents