From 93c9518a07b3d9d34edac62fc80ab9d815ff93f3 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 21 Jan 2026 08:32:52 -0500 Subject: [PATCH 1/4] fix: add system prompt to save Spotify URL to artist socials The update_artist_socials step now has a system prompt instructing the AI to save the Spotify profile URL from the search results. Co-Authored-By: Claude Opus 4.5 --- lib/chat/toolChains/createNewArtistToolChain.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/chat/toolChains/createNewArtistToolChain.ts b/lib/chat/toolChains/createNewArtistToolChain.ts index 3247a84b..17892afa 100644 --- a/lib/chat/toolChains/createNewArtistToolChain.ts +++ b/lib/chat/toolChains/createNewArtistToolChain.ts @@ -8,7 +8,11 @@ export const createNewArtistToolChain: ToolChainItem[] = [ system: "From the get_spotify_search results, select the artist whose name best matches the user-provided artist name (prefer exact, case-insensitive match; otherwise choose the closest by name and popularity). Update the account using the update_account_info tool with the artist's basic information: name, image, label, etc.", }, - { toolName: "update_artist_socials" }, + { + toolName: "update_artist_socials", + system: + "Using the matched Spotify artist from the get_spotify_search results, update the artist's socials with the Spotify profile URL (found in external_urls.spotify). Pass the URL in the urls array to update_artist_socials.", + }, { toolName: "artist_deep_research" }, { toolName: "spotify_deep_research" }, { toolName: "get_artist_socials" }, From 4d4ec6655eb73c4a92ad2a867c6cb37804169d53 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 21 Jan 2026 08:53:04 -0500 Subject: [PATCH 2/4] debug: add logging to updateArtistSocials to trace empty response --- lib/artist/updateArtistSocials.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/artist/updateArtistSocials.ts b/lib/artist/updateArtistSocials.ts index 12fc25c7..cc606043 100644 --- a/lib/artist/updateArtistSocials.ts +++ b/lib/artist/updateArtistSocials.ts @@ -18,20 +18,29 @@ export async function updateArtistSocials( artistId: string, profileUrls: Record, ): Promise { + console.log("[DEBUG] updateArtistSocials called with:", { artistId, profileUrls }); + // Get current account socials (with no limit to get all) const accountSocials = await selectAccountSocials(artistId, 0, 10000); + console.log("[DEBUG] Current accountSocials:", accountSocials?.length || 0); // Process each platform type const profilePromises = Object.entries(profileUrls).map(async ([type, value]) => { + console.log("[DEBUG] Processing platform:", { type, value }); + const socials = value ? await selectSocials({ profile_url: value }) : null; const social = socials && socials.length > 0 ? socials[0] : null; + console.log("[DEBUG] Existing social found:", social?.id || "none"); + const existingSocial = (accountSocials || []).find( (account_social: AccountSocialWithSocial) => getSocialPlatformByLink(account_social.social?.profile_url || "") === type, ); + console.log("[DEBUG] Existing account_social for platform:", existingSocial?.id || "none"); // Delete existing social for this platform type if it exists if (existingSocial && existingSocial.social?.id) { + console.log("[DEBUG] Deleting existing account_social:", existingSocial.social.id); await deleteAccountSocial(artistId, existingSocial.social.id); } @@ -42,19 +51,27 @@ export async function updateArtistSocials( const existing = (accountSocials || []).find( (as: AccountSocialWithSocial) => as.social_id === social.id, ); + console.log("[DEBUG] Social exists, account_social exists:", !!existing); if (!existing) { - await insertAccountSocial(artistId, social.id); + const inserted = await insertAccountSocial(artistId, social.id); + console.log("[DEBUG] Inserted account_social:", inserted?.id || "failed"); } } else { // Create new social record + const username = getUsernameFromProfileUrl(value); + console.log("[DEBUG] Creating new social with username:", username); const newSocials = await insertSocials([ { - username: getUsernameFromProfileUrl(value), + username, profile_url: value, }, ]); + console.log("[DEBUG] insertSocials result:", newSocials.length, newSocials); if (newSocials.length > 0) { - await insertAccountSocial(artistId, newSocials[0].id); + const inserted = await insertAccountSocial(artistId, newSocials[0].id); + console.log("[DEBUG] Inserted account_social for new social:", inserted?.id || "failed"); + } else { + console.log("[DEBUG] insertSocials returned empty - insert may have failed"); } } } @@ -64,5 +81,6 @@ export async function updateArtistSocials( // Return the updated account socials const updated = await selectAccountSocials(artistId, 0, 10000); + console.log("[DEBUG] Final updated accountSocials:", updated?.length || 0, updated); return updated || []; } From 0038cc4b3443310753cc5c1466c184df33e43079 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 21 Jan 2026 09:00:19 -0500 Subject: [PATCH 3/4] fix: normalize profile URLs to match database format The database stores profile URLs without the protocol (e.g., "open.spotify.com/artist/123" instead of "https://..."). The selectSocials query was using the full URL with protocol, causing it to miss existing records. When insertSocials was then called, it failed with a duplicate key violation. - Add normalizeProfileUrl function to strip protocol and trailing slash - Use normalized URLs in updateArtistSocials for both query and insert - Add comprehensive tests for normalizeProfileUrl Co-Authored-By: Claude Opus 4.5 --- lib/artist/updateArtistSocials.ts | 12 ++++--- .../__tests__/normalizeProfileUrl.test.ts | 35 +++++++++++++++++++ lib/socials/normalizeProfileUrl.ts | 14 ++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 lib/socials/__tests__/normalizeProfileUrl.test.ts create mode 100644 lib/socials/normalizeProfileUrl.ts diff --git a/lib/artist/updateArtistSocials.ts b/lib/artist/updateArtistSocials.ts index cc606043..51c6dce8 100644 --- a/lib/artist/updateArtistSocials.ts +++ b/lib/artist/updateArtistSocials.ts @@ -1,5 +1,6 @@ import { getSocialPlatformByLink } from "@/lib/artists/getSocialPlatformByLink"; import { getUsernameFromProfileUrl } from "@/lib/socials/getUsernameFromProfileUrl"; +import { normalizeProfileUrl } from "@/lib/socials/normalizeProfileUrl"; import { selectAccountSocials } from "@/lib/supabase/account_socials/selectAccountSocials"; import { deleteAccountSocial } from "@/lib/supabase/account_socials/deleteAccountSocial"; import { insertAccountSocial } from "@/lib/supabase/account_socials/insertAccountSocial"; @@ -26,9 +27,12 @@ export async function updateArtistSocials( // Process each platform type const profilePromises = Object.entries(profileUrls).map(async ([type, value]) => { - console.log("[DEBUG] Processing platform:", { type, value }); + const normalizedUrl = normalizeProfileUrl(value); + console.log("[DEBUG] Processing platform:", { type, value, normalizedUrl }); - const socials = value ? await selectSocials({ profile_url: value }) : null; + const socials = normalizedUrl + ? await selectSocials({ profile_url: normalizedUrl }) + : null; const social = socials && socials.length > 0 ? socials[0] : null; console.log("[DEBUG] Existing social found:", social?.id || "none"); @@ -45,7 +49,7 @@ export async function updateArtistSocials( } // Insert new social if URL provided - if (value) { + if (normalizedUrl) { if (social) { // Social already exists, check if account_social relationship exists const existing = (accountSocials || []).find( @@ -63,7 +67,7 @@ export async function updateArtistSocials( const newSocials = await insertSocials([ { username, - profile_url: value, + profile_url: normalizedUrl, }, ]); console.log("[DEBUG] insertSocials result:", newSocials.length, newSocials); diff --git a/lib/socials/__tests__/normalizeProfileUrl.test.ts b/lib/socials/__tests__/normalizeProfileUrl.test.ts new file mode 100644 index 00000000..bf198ea6 --- /dev/null +++ b/lib/socials/__tests__/normalizeProfileUrl.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect } from "vitest"; +import { normalizeProfileUrl } from "../normalizeProfileUrl"; + +describe("normalizeProfileUrl", () => { + it("removes https:// protocol from URL", () => { + expect(normalizeProfileUrl("https://open.spotify.com/artist/123")).toBe( + "open.spotify.com/artist/123", + ); + }); + + it("removes http:// protocol from URL", () => { + expect(normalizeProfileUrl("http://twitter.com/user")).toBe("twitter.com/user"); + }); + + it("returns URL unchanged if no protocol", () => { + expect(normalizeProfileUrl("open.spotify.com/artist/123")).toBe( + "open.spotify.com/artist/123", + ); + }); + + it("handles empty string", () => { + expect(normalizeProfileUrl("")).toBe(""); + }); + + it("handles null/undefined", () => { + expect(normalizeProfileUrl(null as unknown as string)).toBe(""); + expect(normalizeProfileUrl(undefined as unknown as string)).toBe(""); + }); + + it("removes trailing slash", () => { + expect(normalizeProfileUrl("https://instagram.com/user/")).toBe( + "instagram.com/user", + ); + }); +}); diff --git a/lib/socials/normalizeProfileUrl.ts b/lib/socials/normalizeProfileUrl.ts new file mode 100644 index 00000000..92f348be --- /dev/null +++ b/lib/socials/normalizeProfileUrl.ts @@ -0,0 +1,14 @@ +/** + * Normalizes a profile URL by removing the protocol and trailing slash. + * This ensures consistent URL format for database queries and storage. + * + * @param url - The profile URL to normalize + * @returns The normalized URL without protocol or trailing slash + */ +export function normalizeProfileUrl(url: string | null | undefined): string { + if (!url) return ""; + + return url + .replace(/^https?:\/\//, "") + .replace(/\/$/, ""); +} From 67836d48bad2bf172b0d9b31a5b71ec6f6615fee Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Wed, 21 Jan 2026 09:03:37 -0500 Subject: [PATCH 4/4] chore: remove debug logging from updateArtistSocials --- lib/artist/updateArtistSocials.ts | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/artist/updateArtistSocials.ts b/lib/artist/updateArtistSocials.ts index 51c6dce8..fc0d63e1 100644 --- a/lib/artist/updateArtistSocials.ts +++ b/lib/artist/updateArtistSocials.ts @@ -19,32 +19,25 @@ export async function updateArtistSocials( artistId: string, profileUrls: Record, ): Promise { - console.log("[DEBUG] updateArtistSocials called with:", { artistId, profileUrls }); - // Get current account socials (with no limit to get all) const accountSocials = await selectAccountSocials(artistId, 0, 10000); - console.log("[DEBUG] Current accountSocials:", accountSocials?.length || 0); // Process each platform type const profilePromises = Object.entries(profileUrls).map(async ([type, value]) => { const normalizedUrl = normalizeProfileUrl(value); - console.log("[DEBUG] Processing platform:", { type, value, normalizedUrl }); const socials = normalizedUrl ? await selectSocials({ profile_url: normalizedUrl }) : null; const social = socials && socials.length > 0 ? socials[0] : null; - console.log("[DEBUG] Existing social found:", social?.id || "none"); const existingSocial = (accountSocials || []).find( (account_social: AccountSocialWithSocial) => getSocialPlatformByLink(account_social.social?.profile_url || "") === type, ); - console.log("[DEBUG] Existing account_social for platform:", existingSocial?.id || "none"); // Delete existing social for this platform type if it exists if (existingSocial && existingSocial.social?.id) { - console.log("[DEBUG] Deleting existing account_social:", existingSocial.social.id); await deleteAccountSocial(artistId, existingSocial.social.id); } @@ -55,27 +48,20 @@ export async function updateArtistSocials( const existing = (accountSocials || []).find( (as: AccountSocialWithSocial) => as.social_id === social.id, ); - console.log("[DEBUG] Social exists, account_social exists:", !!existing); if (!existing) { - const inserted = await insertAccountSocial(artistId, social.id); - console.log("[DEBUG] Inserted account_social:", inserted?.id || "failed"); + await insertAccountSocial(artistId, social.id); } } else { // Create new social record const username = getUsernameFromProfileUrl(value); - console.log("[DEBUG] Creating new social with username:", username); const newSocials = await insertSocials([ { username, profile_url: normalizedUrl, }, ]); - console.log("[DEBUG] insertSocials result:", newSocials.length, newSocials); if (newSocials.length > 0) { - const inserted = await insertAccountSocial(artistId, newSocials[0].id); - console.log("[DEBUG] Inserted account_social for new social:", inserted?.id || "failed"); - } else { - console.log("[DEBUG] insertSocials returned empty - insert may have failed"); + await insertAccountSocial(artistId, newSocials[0].id); } } } @@ -85,6 +71,5 @@ export async function updateArtistSocials( // Return the updated account socials const updated = await selectAccountSocials(artistId, 0, 10000); - console.log("[DEBUG] Final updated accountSocials:", updated?.length || 0, updated); return updated || []; }