Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions app/api/accounts/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,10 @@ export async function OPTIONS() {
* - id (required): The unique identifier of the account (UUID)
*
* @param request - The request object
* @param params.params
* @param params - Route params containing the account ID
* @returns A NextResponse with account data
*/
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
return getAccountHandler(request, params);
}

1 change: 0 additions & 1 deletion app/api/artists/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,3 @@ export async function GET(request: NextRequest) {
export async function POST(request: NextRequest) {
return createArtistPostHandler(request);
}

4 changes: 2 additions & 2 deletions app/api/connectors/authorize/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export async function OPTIONS() {
* - connector: The connector slug, e.g., "googlesheets" (required)
* - callback_url: Optional custom callback URL after OAuth
*
* @param request
* @returns The redirect URL for OAuth authorization
*/
export async function POST(request: NextRequest): Promise<NextResponse> {
Expand Down Expand Up @@ -60,8 +61,7 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
{ status: 200, headers },
);
} catch (error) {
const message =
error instanceof Error ? error.message : "Failed to authorize connector";
const message = error instanceof Error ? error.message : "Failed to authorize connector";
return NextResponse.json({ error: message }, { status: 500, headers });
}
}
11 changes: 6 additions & 5 deletions app/api/connectors/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export async function OPTIONS() {
*
* Authentication: x-api-key header required.
*
* @param request
* @returns List of connectors with connection status
*/
export async function GET(request: NextRequest): Promise<NextResponse> {
Expand All @@ -49,8 +50,7 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
{ status: 200, headers },
);
} catch (error) {
const message =
error instanceof Error ? error.message : "Failed to fetch connectors";
const message = error instanceof Error ? error.message : "Failed to fetch connectors";
return NextResponse.json({ error: message }, { status: 500, headers });
}
}
Expand All @@ -63,6 +63,8 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
* Authentication: x-api-key header required.
*
* Body: { connected_account_id: string }
*
* @param request
*/
export async function DELETE(request: NextRequest): Promise<NextResponse> {
const headers = getCorsHeaders();
Expand All @@ -88,7 +90,7 @@ export async function DELETE(request: NextRequest): Promise<NextResponse> {
if (!isOwner) {
return NextResponse.json(
{ error: "Connected account not found or does not belong to this user" },
{ status: 403, headers }
{ status: 403, headers },
);
}

Expand All @@ -102,8 +104,7 @@ export async function DELETE(request: NextRequest): Promise<NextResponse> {
{ status: 200, headers },
);
} catch (error) {
const message =
error instanceof Error ? error.message : "Failed to disconnect connector";
const message = error instanceof Error ? error.message : "Failed to disconnect connector";
return NextResponse.json({ error: message }, { status: 500, headers });
}
}
1 change: 0 additions & 1 deletion app/api/organizations/artists/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,3 @@ export async function OPTIONS() {
export async function POST(request: NextRequest) {
return addArtistToOrgHandler(request);
}

1 change: 0 additions & 1 deletion app/api/organizations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,3 @@ export async function GET(request: NextRequest) {
export async function POST(request: NextRequest) {
return createOrganizationHandler(request);
}

1 change: 0 additions & 1 deletion app/api/spotify/artist/albums/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,3 @@ export async function OPTIONS() {
export async function GET(request: NextRequest) {
return getSpotifyArtistAlbumsHandler(request);
}

5 changes: 4 additions & 1 deletion app/api/transcribe/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { NextRequest, NextResponse } from "next/server";
import { processAudioTranscription } from "@/lib/transcribe/processAudioTranscription";
import { formatTranscriptionError } from "@/lib/transcribe/types";

/**
*
* @param req
*/
export async function POST(req: NextRequest) {
try {
const body = await req.json();
Expand Down Expand Up @@ -40,4 +44,3 @@ export async function POST(req: NextRequest) {
return NextResponse.json({ error: message }, { status });
}
}

42 changes: 42 additions & 0 deletions app/api/workspaces/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { NextRequest, NextResponse } from "next/server";
import { getCorsHeaders } from "@/lib/networking/getCorsHeaders";
import { createWorkspacePostHandler } from "@/lib/workspaces/createWorkspacePostHandler";

/**
* 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/workspaces
*
* Creates a new workspace account.
*
* Request body:
* - name (optional): The name of the workspace to create. Defaults to "Untitled".
* - account_id (optional): The ID of the account to create the workspace for (UUID).
* Only required for organization API keys creating workspaces on behalf of other accounts.
* - organization_id (optional): The organization ID to link the new workspace to (UUID).
* If provided, the workspace will appear in that organization's view.
* Access is validated to ensure the user has access to the organization.
*
* Response:
* - 201: { workspace: WorkspaceObject }
* - 400: { status: "error", error: "validation error message" }
* - 401: { status: "error", error: "x-api-key header required" or "Invalid API key" }
* - 403: { status: "error", error: "Access denied to specified organization_id/account_id" }
* - 500: { status: "error", error: "Failed to create workspace" }
*
* @param request - The request object containing JSON body
* @returns A NextResponse with the created workspace data (201) or error
*/
export async function POST(request: NextRequest) {
return createWorkspacePostHandler(request);
}
1 change: 0 additions & 1 deletion lib/accounts/getAccountHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,3 @@ export async function getAccountHandler(
);
}
}

1 change: 0 additions & 1 deletion lib/accounts/validateAccountParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,3 @@ export function validateAccountParams(id: string): NextResponse | AccountParams

return result.data;
}

22 changes: 11 additions & 11 deletions lib/agents/generalAgent/__tests__/getGeneralAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
import { ChatRequestBody } from "@/lib/chat/validateChatRequest";
import { ToolLoopAgent, stepCountIs } from "ai";

// Import after mocks
import getGeneralAgent from "../getGeneralAgent";
import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails";
import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo";
import { getAccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetails";
import { getKnowledgeBaseText } from "@/lib/files/getKnowledgeBaseText";
import { setupToolsForRequest } from "@/lib/chat/setupToolsForRequest";
import { getSystemPrompt } from "@/lib/prompts/getSystemPrompt";
import { extractImageUrlsFromMessages } from "@/lib/messages/extractImageUrlsFromMessages";
import { buildSystemPromptWithImages } from "@/lib/chat/buildSystemPromptWithImages";

// Mock all external dependencies
vi.mock("@/lib/supabase/account_emails/selectAccountEmails", () => ({
default: vi.fn(),
Expand Down Expand Up @@ -35,17 +46,6 @@ vi.mock("@/lib/chat/buildSystemPromptWithImages", () => ({
buildSystemPromptWithImages: vi.fn(),
}));

// Import after mocks
import getGeneralAgent from "../getGeneralAgent";
import selectAccountEmails from "@/lib/supabase/account_emails/selectAccountEmails";
import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo";
import { getAccountWithDetails } from "@/lib/supabase/accounts/getAccountWithDetails";
import { getKnowledgeBaseText } from "@/lib/files/getKnowledgeBaseText";
import { setupToolsForRequest } from "@/lib/chat/setupToolsForRequest";
import { getSystemPrompt } from "@/lib/prompts/getSystemPrompt";
import { extractImageUrlsFromMessages } from "@/lib/messages/extractImageUrlsFromMessages";
import { buildSystemPromptWithImages } from "@/lib/chat/buildSystemPromptWithImages";

const mockSelectAccountEmails = vi.mocked(selectAccountEmails);
const mockSelectAccountInfo = vi.mocked(selectAccountInfo);
const mockGetAccountWithDetails = vi.mocked(getAccountWithDetails);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { ChatRequestBody } from "@/lib/chat/validateChatRequest";

// Import after mocks
import getGoogleSheetsTools from "../getGoogleSheetsTools";
import { getComposioClient } from "@/lib/composio/client";
import getConnectedAccount from "@/lib/composio/googleSheets/getConnectedAccount";
import getLatestUserMessageText from "@/lib/messages/getLatestUserMessageText";

// Mock external dependencies
vi.mock("@/lib/composio/client", () => ({
getComposioClient: vi.fn(),
Expand All @@ -19,12 +25,6 @@ vi.mock("@/lib/composio/tools/googleSheetsLoginTool", () => ({
default: { description: "Login to Google Sheets", parameters: {} },
}));

// Import after mocks
import getGoogleSheetsTools from "../getGoogleSheetsTools";
import { getComposioClient } from "@/lib/composio/client";
import getConnectedAccount from "@/lib/composio/googleSheets/getConnectedAccount";
import getLatestUserMessageText from "@/lib/messages/getLatestUserMessageText";

const mockGetComposioClient = vi.mocked(getComposioClient);
const mockGetConnectedAccount = vi.mocked(getConnectedAccount);
const mockGetLatestUserMessageText = vi.mocked(getLatestUserMessageText);
Expand Down Expand Up @@ -62,10 +62,7 @@ describe("getGoogleSheetsTools", () => {

await getGoogleSheetsTools(body);

expect(mockGetConnectedAccount).toHaveBeenCalledWith(
"account-123",
expect.any(Object),
);
expect(mockGetConnectedAccount).toHaveBeenCalledWith("account-123", expect.any(Object));
});

it("passes callback URL with encoded latest user message", async () => {
Expand Down
8 changes: 4 additions & 4 deletions lib/ai/__tests__/getAvailableModels.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

import { gateway } from "@ai-sdk/gateway";
import { getAvailableModels } from "../getAvailableModels";

vi.mock("@ai-sdk/gateway", () => ({
gateway: {
getAvailableModels: vi.fn(),
},
}));

import { gateway } from "@ai-sdk/gateway";
import { getAvailableModels } from "../getAvailableModels";

const mockGatewayGetAvailableModels = vi.mocked(gateway.getAvailableModels);

describe("getAvailableModels", () => {
Expand All @@ -35,7 +35,7 @@ describe("getAvailableModels", () => {

// Should filter out embed models (output price = 0)
expect(models).toHaveLength(2);
expect(models.map((m) => m.id)).toEqual(["gpt-4", "claude-3-opus"]);
expect(models.map(m => m.id)).toEqual(["gpt-4", "claude-3-opus"]);
});

it("returns empty array when gateway returns no models", async () => {
Expand Down
10 changes: 4 additions & 6 deletions lib/ai/__tests__/getModel.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";

import { getAvailableModels } from "@/lib/ai/getAvailableModels";
import { getModel } from "../getModel";

vi.mock("@/lib/ai/getAvailableModels", () => ({
getAvailableModels: vi.fn(),
}));

import { getAvailableModels } from "@/lib/ai/getAvailableModels";
import { getModel } from "../getModel";

const mockGetAvailableModels = vi.mocked(getAvailableModels);

describe("getModel", () => {
Expand Down Expand Up @@ -35,9 +35,7 @@ describe("getModel", () => {
});

it("returns undefined when model is not found", async () => {
const models = [
{ id: "gpt-4", pricing: { input: "0.00003", output: "0.00006" } },
];
const models = [{ id: "gpt-4", pricing: { input: "0.00003", output: "0.00006" } }];
mockGetAvailableModels.mockResolvedValue(models as any);

const model = await getModel("unknown-model");
Expand Down
6 changes: 2 additions & 4 deletions lib/ai/getAvailableModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import isEmbedModel from "./isEmbedModel";
* Returns the list of available LLMs from the Vercel AI Gateway.
* Filters out embed models that are not suitable for chat.
*/
export const getAvailableModels = async (): Promise<
GatewayLanguageModelEntry[]
> => {
export const getAvailableModels = async (): Promise<GatewayLanguageModelEntry[]> => {
try {
const apiResponse = await gateway.getAvailableModels();
const gatewayModels = apiResponse.models.filter((m) => !isEmbedModel(m));
const gatewayModels = apiResponse.models.filter(m => !isEmbedModel(m));
return gatewayModels;
} catch {
return [];
Expand Down
7 changes: 3 additions & 4 deletions lib/ai/getModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { GatewayLanguageModelEntry } from "@ai-sdk/gateway";

/**
* Returns a specific model by its ID from the list of available models.
*
* @param modelId - The ID of the model to find
* @returns The matching model or undefined if not found
*/
export const getModel = async (
modelId: string,
): Promise<GatewayLanguageModelEntry | undefined> => {
export const getModel = async (modelId: string): Promise<GatewayLanguageModelEntry | undefined> => {
try {
const availableModels = await getAvailableModels();
return availableModels.find((model) => model.id === modelId);
return availableModels.find(model => model.id === modelId);
} catch (error) {
console.error(`Failed to get model with ID ${modelId}:`, error);
return undefined;
Expand Down
2 changes: 2 additions & 0 deletions lib/ai/isEmbedModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { GatewayLanguageModelEntry } from "@ai-sdk/gateway";
/**
* Determines if a model is an embedding model (not suitable for chat).
* Embed models typically have 0 output pricing since they only produce embeddings.
*
* @param m
*/
export const isEmbedModel = (m: GatewayLanguageModelEntry): boolean => {
const pricing = m.pricing;
Expand Down
1 change: 0 additions & 1 deletion lib/artist/validateArtistSocialsScrapeBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@ export function validateArtistSocialsScrapeBody(

return validationResult.data;
}

4 changes: 2 additions & 2 deletions lib/artists/__tests__/createArtistInDb.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { describe, it, expect, vi, beforeEach } from "vitest";

import { createArtistInDb } from "../createArtistInDb";

const mockInsertAccount = vi.fn();
const mockInsertAccountInfo = vi.fn();
const mockSelectAccountWithSocials = vi.fn();
Expand All @@ -26,8 +28,6 @@ vi.mock("@/lib/supabase/artist_organization_ids/addArtistToOrganization", () =>
addArtistToOrganization: (...args: unknown[]) => mockAddArtistToOrganization(...args),
}));

import { createArtistInDb } from "../createArtistInDb";

describe("createArtistInDb", () => {
const mockAccount = {
id: "artist-123",
Expand Down
Loading
Loading