From 281af71ea0899a62758606e0036ad1a0675af185 Mon Sep 17 00:00:00 2001 From: alex-mcgovern Date: Thu, 6 Mar 2025 08:52:02 +0000 Subject: [PATCH 1/6] fix: handle breaking changes to "workspaces by provider" endpoint --- .../generated/@tanstack/react-query.gen.ts | 216 +++++++-- src/api/generated/sdk.gen.ts | 175 +++++++- src/api/generated/types.gen.ts | 124 +++++- src/api/openapi.json | 416 +++++++++++++++--- src/context/confirm-context.tsx | 9 +- .../components/workspaces-by-provider.tsx | 4 +- .../hooks/use-confirm-delete-provider.tsx | 7 +- .../hooks/use-invalidate-providers-queries.ts | 4 +- .../hooks/use-query-workspaces-by-provider.ts | 4 +- .../hooks/use-invalidate-workspace-queries.ts | 2 - 10 files changed, 819 insertions(+), 142 deletions(-) diff --git a/src/api/generated/@tanstack/react-query.gen.ts b/src/api/generated/@tanstack/react-query.gen.ts index cf1307ef..1098cb0d 100644 --- a/src/api/generated/@tanstack/react-query.gen.ts +++ b/src/api/generated/@tanstack/react-query.gen.ts @@ -19,20 +19,26 @@ import { v1ActivateWorkspace, v1UpdateWorkspace, v1DeleteWorkspace, + v1GetWorkspaceByName, v1ListArchivedWorkspaces, v1RecoverWorkspace, v1HardDeleteWorkspace, v1GetWorkspaceAlerts, + v1GetWorkspaceAlertsSummary, v1GetWorkspaceMessages, v1GetWorkspaceCustomInstructions, v1SetWorkspaceCustomInstructions, v1DeleteWorkspaceCustomInstructions, v1GetWorkspaceMuxes, v1SetWorkspaceMuxes, - v1ListWorkspacesByProvider, v1StreamSse, v1VersionCheck, v1GetWorkspaceTokenUsage, + v1ListPersonas, + v1CreatePersona, + v1GetPersona, + v1UpdatePersona, + v1DeletePersona, } from '../sdk.gen' import type { V1ListProviderEndpointsData, @@ -50,6 +56,7 @@ import type { V1ConfigureAuthMaterialData, V1ConfigureAuthMaterialError, V1ConfigureAuthMaterialResponse, + V1ListWorkspacesData, V1CreateWorkspaceData, V1CreateWorkspaceError, V1CreateWorkspaceResponse, @@ -62,6 +69,7 @@ import type { V1DeleteWorkspaceData, V1DeleteWorkspaceError, V1DeleteWorkspaceResponse, + V1GetWorkspaceByNameData, V1RecoverWorkspaceData, V1RecoverWorkspaceError, V1RecoverWorkspaceResponse, @@ -69,6 +77,7 @@ import type { V1HardDeleteWorkspaceError, V1HardDeleteWorkspaceResponse, V1GetWorkspaceAlertsData, + V1GetWorkspaceAlertsSummaryData, V1GetWorkspaceMessagesData, V1GetWorkspaceCustomInstructionsData, V1SetWorkspaceCustomInstructionsData, @@ -81,8 +90,17 @@ import type { V1SetWorkspaceMuxesData, V1SetWorkspaceMuxesError, V1SetWorkspaceMuxesResponse, - V1ListWorkspacesByProviderData, V1GetWorkspaceTokenUsageData, + V1CreatePersonaData, + V1CreatePersonaError, + V1CreatePersonaResponse, + V1GetPersonaData, + V1UpdatePersonaData, + V1UpdatePersonaError, + V1UpdatePersonaResponse, + V1DeletePersonaData, + V1DeletePersonaError, + V1DeletePersonaResponse, } from '../types.gen' type QueryKey = [ @@ -323,11 +341,13 @@ export const v1ConfigureAuthMaterialMutation = ( return mutationOptions } -export const v1ListWorkspacesQueryKey = (options?: OptionsLegacyParser) => [ - createQueryKey('v1ListWorkspaces', options), -] +export const v1ListWorkspacesQueryKey = ( + options?: OptionsLegacyParser +) => [createQueryKey('v1ListWorkspaces', options)] -export const v1ListWorkspacesOptions = (options?: OptionsLegacyParser) => { +export const v1ListWorkspacesOptions = ( + options?: OptionsLegacyParser +) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { const { data } = await v1ListWorkspaces({ @@ -485,6 +505,27 @@ export const v1DeleteWorkspaceMutation = ( return mutationOptions } +export const v1GetWorkspaceByNameQueryKey = ( + options: OptionsLegacyParser +) => [createQueryKey('v1GetWorkspaceByName', options)] + +export const v1GetWorkspaceByNameOptions = ( + options: OptionsLegacyParser +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1GetWorkspaceByName({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }) + return data + }, + queryKey: v1GetWorkspaceByNameQueryKey(options), + }) +} + export const v1ListArchivedWorkspacesQueryKey = ( options?: OptionsLegacyParser ) => [createQueryKey('v1ListArchivedWorkspaces', options)] @@ -588,6 +629,27 @@ export const v1GetWorkspaceAlertsOptions = ( }) } +export const v1GetWorkspaceAlertsSummaryQueryKey = ( + options: OptionsLegacyParser +) => [createQueryKey('v1GetWorkspaceAlertsSummary', options)] + +export const v1GetWorkspaceAlertsSummaryOptions = ( + options: OptionsLegacyParser +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1GetWorkspaceAlertsSummary({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }) + return data + }, + queryKey: v1GetWorkspaceAlertsSummaryQueryKey(options), + }) +} + export const v1GetWorkspaceMessagesQueryKey = ( options: OptionsLegacyParser ) => [createQueryKey('v1GetWorkspaceMessages', options)] @@ -713,27 +775,6 @@ export const v1SetWorkspaceMuxesMutation = ( return mutationOptions } -export const v1ListWorkspacesByProviderQueryKey = ( - options: OptionsLegacyParser -) => [createQueryKey('v1ListWorkspacesByProvider', options)] - -export const v1ListWorkspacesByProviderOptions = ( - options: OptionsLegacyParser -) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await v1ListWorkspacesByProvider({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }) - return data - }, - queryKey: v1ListWorkspacesByProviderQueryKey(options), - }) -} - export const v1StreamSseQueryKey = (options?: OptionsLegacyParser) => [ createQueryKey('v1StreamSse', options), ] @@ -792,3 +833,124 @@ export const v1GetWorkspaceTokenUsageOptions = ( queryKey: v1GetWorkspaceTokenUsageQueryKey(options), }) } + +export const v1ListPersonasQueryKey = (options?: OptionsLegacyParser) => [ + createQueryKey('v1ListPersonas', options), +] + +export const v1ListPersonasOptions = (options?: OptionsLegacyParser) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1ListPersonas({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }) + return data + }, + queryKey: v1ListPersonasQueryKey(options), + }) +} + +export const v1CreatePersonaQueryKey = ( + options: OptionsLegacyParser +) => [createQueryKey('v1CreatePersona', options)] + +export const v1CreatePersonaOptions = ( + options: OptionsLegacyParser +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1CreatePersona({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }) + return data + }, + queryKey: v1CreatePersonaQueryKey(options), + }) +} + +export const v1CreatePersonaMutation = ( + options?: Partial> +) => { + const mutationOptions: UseMutationOptions< + V1CreatePersonaResponse, + V1CreatePersonaError, + OptionsLegacyParser + > = { + mutationFn: async (localOptions) => { + const { data } = await v1CreatePersona({ + ...options, + ...localOptions, + throwOnError: true, + }) + return data + }, + } + return mutationOptions +} + +export const v1GetPersonaQueryKey = ( + options: OptionsLegacyParser +) => [createQueryKey('v1GetPersona', options)] + +export const v1GetPersonaOptions = ( + options: OptionsLegacyParser +) => { + return queryOptions({ + queryFn: async ({ queryKey, signal }) => { + const { data } = await v1GetPersona({ + ...options, + ...queryKey[0], + signal, + throwOnError: true, + }) + return data + }, + queryKey: v1GetPersonaQueryKey(options), + }) +} + +export const v1UpdatePersonaMutation = ( + options?: Partial> +) => { + const mutationOptions: UseMutationOptions< + V1UpdatePersonaResponse, + V1UpdatePersonaError, + OptionsLegacyParser + > = { + mutationFn: async (localOptions) => { + const { data } = await v1UpdatePersona({ + ...options, + ...localOptions, + throwOnError: true, + }) + return data + }, + } + return mutationOptions +} + +export const v1DeletePersonaMutation = ( + options?: Partial> +) => { + const mutationOptions: UseMutationOptions< + V1DeletePersonaResponse, + V1DeletePersonaError, + OptionsLegacyParser + > = { + mutationFn: async (localOptions) => { + const { data } = await v1DeletePersona({ + ...options, + ...localOptions, + throwOnError: true, + }) + return data + }, + } + return mutationOptions +} diff --git a/src/api/generated/sdk.gen.ts b/src/api/generated/sdk.gen.ts index 4b8998a7..7341d1bf 100644 --- a/src/api/generated/sdk.gen.ts +++ b/src/api/generated/sdk.gen.ts @@ -31,6 +31,7 @@ import type { V1ConfigureAuthMaterialData, V1ConfigureAuthMaterialError, V1ConfigureAuthMaterialResponse, + V1ListWorkspacesData, V1ListWorkspacesError, V1ListWorkspacesResponse, V1CreateWorkspaceData, @@ -47,6 +48,9 @@ import type { V1DeleteWorkspaceData, V1DeleteWorkspaceError, V1DeleteWorkspaceResponse, + V1GetWorkspaceByNameData, + V1GetWorkspaceByNameError, + V1GetWorkspaceByNameResponse, V1ListArchivedWorkspacesError, V1ListArchivedWorkspacesResponse, V1RecoverWorkspaceData, @@ -58,6 +62,9 @@ import type { V1GetWorkspaceAlertsData, V1GetWorkspaceAlertsError, V1GetWorkspaceAlertsResponse, + V1GetWorkspaceAlertsSummaryData, + V1GetWorkspaceAlertsSummaryError, + V1GetWorkspaceAlertsSummaryResponse, V1GetWorkspaceMessagesData, V1GetWorkspaceMessagesError, V1GetWorkspaceMessagesResponse, @@ -76,9 +83,6 @@ import type { V1SetWorkspaceMuxesData, V1SetWorkspaceMuxesError, V1SetWorkspaceMuxesResponse, - V1ListWorkspacesByProviderData, - V1ListWorkspacesByProviderError, - V1ListWorkspacesByProviderResponse, V1StreamSseError, V1StreamSseResponse, V1VersionCheckError, @@ -86,6 +90,20 @@ import type { V1GetWorkspaceTokenUsageData, V1GetWorkspaceTokenUsageError, V1GetWorkspaceTokenUsageResponse, + V1ListPersonasError, + V1ListPersonasResponse, + V1CreatePersonaData, + V1CreatePersonaError, + V1CreatePersonaResponse, + V1GetPersonaData, + V1GetPersonaError, + V1GetPersonaResponse, + V1UpdatePersonaData, + V1UpdatePersonaError, + V1UpdatePersonaResponse, + V1DeletePersonaData, + V1DeletePersonaError, + V1DeletePersonaResponse, } from './types.gen' export const client = createClient(createConfig()) @@ -247,9 +265,18 @@ export const v1ConfigureAuthMaterial = ( /** * List Workspaces * List all workspaces. + * + * Args: + * provider_id (Optional[UUID]): Filter workspaces by provider ID. If provided, + * will return workspaces where models from the specified provider (e.g., OpenAI, + * Anthropic) have been used in workspace muxing rules. Note that you must + * refer to a provider by ID, not by name. + * + * Returns: + * ListWorkspacesResponse: A response object containing the list of workspaces. */ export const v1ListWorkspaces = ( - options?: OptionsLegacyParser + options?: OptionsLegacyParser ) => { return (options?.client ?? client).get< V1ListWorkspacesResponse, @@ -349,6 +376,23 @@ export const v1DeleteWorkspace = ( }) } +/** + * Get Workspace By Name + * List workspaces by provider ID. + */ +export const v1GetWorkspaceByName = ( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).get< + V1GetWorkspaceByNameResponse, + V1GetWorkspaceByNameError, + ThrowOnError + >({ + ...options, + url: '/api/v1/workspaces/{workspace_name}', + }) +} + /** * List Archived Workspaces * List all archived workspaces. @@ -417,6 +461,25 @@ export const v1GetWorkspaceAlerts = ( }) } +/** + * Get Workspace Alerts Summary + * Get alert summary for a workspace. + */ +export const v1GetWorkspaceAlertsSummary = < + ThrowOnError extends boolean = false, +>( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).get< + V1GetWorkspaceAlertsSummaryResponse, + V1GetWorkspaceAlertsSummaryError, + ThrowOnError + >({ + ...options, + url: '/api/v1/workspaces/{workspace_name}/alerts-summary', + }) +} + /** * Get Workspace Messages * Get messages for a workspace. @@ -535,25 +598,6 @@ export const v1SetWorkspaceMuxes = ( }) } -/** - * List Workspaces By Provider - * List workspaces by provider ID. - */ -export const v1ListWorkspacesByProvider = < - ThrowOnError extends boolean = false, ->( - options: OptionsLegacyParser -) => { - return (options?.client ?? client).get< - V1ListWorkspacesByProviderResponse, - V1ListWorkspacesByProviderError, - ThrowOnError - >({ - ...options, - url: '/api/v1/workspaces/{provider_id}', - }) -} - /** * Stream Sse * Send alerts event @@ -603,3 +647,88 @@ export const v1GetWorkspaceTokenUsage = ( url: '/api/v1/workspaces/{workspace_name}/token-usage', }) } + +/** + * List Personas + * List all personas. + */ +export const v1ListPersonas = ( + options?: OptionsLegacyParser +) => { + return (options?.client ?? client).get< + V1ListPersonasResponse, + V1ListPersonasError, + ThrowOnError + >({ + ...options, + url: '/api/v1/personas', + }) +} + +/** + * Create Persona + * Create a new persona. + */ +export const v1CreatePersona = ( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).post< + V1CreatePersonaResponse, + V1CreatePersonaError, + ThrowOnError + >({ + ...options, + url: '/api/v1/personas', + }) +} + +/** + * Get Persona + * Get a persona by name. + */ +export const v1GetPersona = ( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).get< + V1GetPersonaResponse, + V1GetPersonaError, + ThrowOnError + >({ + ...options, + url: '/api/v1/personas/{persona_name}', + }) +} + +/** + * Update Persona + * Update an existing persona. + */ +export const v1UpdatePersona = ( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).put< + V1UpdatePersonaResponse, + V1UpdatePersonaError, + ThrowOnError + >({ + ...options, + url: '/api/v1/personas/{persona_name}', + }) +} + +/** + * Delete Persona + * Delete a persona. + */ +export const v1DeletePersona = ( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).delete< + V1DeletePersonaResponse, + V1DeletePersonaError, + ThrowOnError + >({ + ...options, + url: '/api/v1/personas/{persona_name}', + }) +} diff --git a/src/api/generated/types.gen.ts b/src/api/generated/types.gen.ts index 896e4bc3..7d446a7a 100644 --- a/src/api/generated/types.gen.ts +++ b/src/api/generated/types.gen.ts @@ -64,6 +64,15 @@ export enum AlertSeverity { CRITICAL = 'critical', } +/** + * Represents a set of summary alerts + */ +export type AlertSummary = { + malicious_packages: number + pii: number + secrets: number +} + /** * Represents a chat message. */ @@ -178,6 +187,31 @@ export type MuxRule = { matcher?: string | null } +/** + * Represents a persona object. + */ +export type Persona = { + id: string + name: string + description: string +} + +/** + * Model for creating a new Persona. + */ +export type PersonaRequest = { + name: string + description: string +} + +/** + * Model for updating a Persona. + */ +export type PersonaUpdateRequest = { + new_name: string + new_description: string +} + /** * Represents the different types of auth we support for providers. */ @@ -279,15 +313,6 @@ export type WorkspaceConfig_Output = { muxing_rules: Array } -/** - * Returns a workspace ID with model name - */ -export type WorkspaceWithModel = { - id: string - name: string - provider_model_name: string -} - export type HealthCheckHealthGetResponse = unknown export type HealthCheckHealthGetError = unknown @@ -366,9 +391,15 @@ export type V1ConfigureAuthMaterialResponse = void export type V1ConfigureAuthMaterialError = HTTPValidationError +export type V1ListWorkspacesData = { + query?: { + provider_id?: string | null + } +} + export type V1ListWorkspacesResponse = ListWorkspacesResponse -export type V1ListWorkspacesError = unknown +export type V1ListWorkspacesError = HTTPValidationError export type V1CreateWorkspaceData = { body: FullWorkspace_Input @@ -414,6 +445,16 @@ export type V1DeleteWorkspaceResponse = unknown export type V1DeleteWorkspaceError = HTTPValidationError +export type V1GetWorkspaceByNameData = { + path: { + workspace_name: string + } +} + +export type V1GetWorkspaceByNameResponse = FullWorkspace_Output + +export type V1GetWorkspaceByNameError = HTTPValidationError + export type V1ListArchivedWorkspacesResponse = ListWorkspacesResponse export type V1ListArchivedWorkspacesError = unknown @@ -448,6 +489,16 @@ export type V1GetWorkspaceAlertsResponse = Array export type V1GetWorkspaceAlertsError = HTTPValidationError +export type V1GetWorkspaceAlertsSummaryData = { + path: { + workspace_name: string + } +} + +export type V1GetWorkspaceAlertsSummaryResponse = AlertSummary + +export type V1GetWorkspaceAlertsSummaryError = HTTPValidationError + export type V1GetWorkspaceMessagesData = { path: { workspace_name: string @@ -510,16 +561,6 @@ export type V1SetWorkspaceMuxesResponse = void export type V1SetWorkspaceMuxesError = HTTPValidationError -export type V1ListWorkspacesByProviderData = { - path: { - provider_id: string - } -} - -export type V1ListWorkspacesByProviderResponse = Array - -export type V1ListWorkspacesByProviderError = HTTPValidationError - export type V1StreamSseResponse = unknown export type V1StreamSseError = unknown @@ -537,3 +578,46 @@ export type V1GetWorkspaceTokenUsageData = { export type V1GetWorkspaceTokenUsageResponse = TokenUsageAggregate export type V1GetWorkspaceTokenUsageError = HTTPValidationError + +export type V1ListPersonasResponse = Array + +export type V1ListPersonasError = unknown + +export type V1CreatePersonaData = { + body: PersonaRequest +} + +export type V1CreatePersonaResponse = Persona + +export type V1CreatePersonaError = HTTPValidationError + +export type V1GetPersonaData = { + path: { + persona_name: string + } +} + +export type V1GetPersonaResponse = Persona + +export type V1GetPersonaError = HTTPValidationError + +export type V1UpdatePersonaData = { + body: PersonaUpdateRequest + path: { + persona_name: string + } +} + +export type V1UpdatePersonaResponse = Persona + +export type V1UpdatePersonaError = HTTPValidationError + +export type V1DeletePersonaData = { + path: { + persona_name: string + } +} + +export type V1DeletePersonaResponse = void + +export type V1DeletePersonaError = HTTPValidationError diff --git a/src/api/openapi.json b/src/api/openapi.json index c636d394..0ffc1d51 100644 --- a/src/api/openapi.json +++ b/src/api/openapi.json @@ -362,8 +362,27 @@ "get": { "tags": ["CodeGate API", "Workspaces"], "summary": "List Workspaces", - "description": "List all workspaces.", + "description": "List all workspaces.\n\nArgs:\n provider_id (Optional[UUID]): Filter workspaces by provider ID. If provided,\n will return workspaces where models from the specified provider (e.g., OpenAI,\n Anthropic) have been used in workspace muxing rules. Note that you must\n refer to a provider by ID, not by name.\n\nReturns:\n ListWorkspacesResponse: A response object containing the list of workspaces.", "operationId": "v1_list_workspaces", + "parameters": [ + { + "name": "provider_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "uuid" + }, + { + "type": "null" + } + ], + "title": "Provider Id" + } + } + ], "responses": { "200": { "description": "Successful Response", @@ -374,6 +393,16 @@ } } } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } } } }, @@ -383,14 +412,14 @@ "description": "Create a new workspace.", "operationId": "v1_create_workspace", "requestBody": { + "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FullWorkspace-Input" } } - }, - "required": true + } }, "responses": { "201": { @@ -569,6 +598,45 @@ } } } + }, + "get": { + "tags": ["CodeGate API", "Workspaces"], + "summary": "Get Workspace By Name", + "description": "List workspaces by provider ID.", + "operationId": "v1_get_workspace_by_name", + "parameters": [ + { + "name": "workspace_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Workspace Name" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FullWorkspace-Output" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } } }, "/api/v1/workspaces/archive": { @@ -716,6 +784,47 @@ } } }, + "/api/v1/workspaces/{workspace_name}/alerts-summary": { + "get": { + "tags": ["CodeGate API", "Workspaces"], + "summary": "Get Workspace Alerts Summary", + "description": "Get alert summary for a workspace.", + "operationId": "v1_get_workspace_alerts_summary", + "parameters": [ + { + "name": "workspace_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Workspace Name" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlertSummary" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, "/api/v1/workspaces/{workspace_name}/messages": { "get": { "tags": ["CodeGate API", "Workspaces"], @@ -965,21 +1074,55 @@ } } }, - "/api/v1/workspaces/{provider_id}": { + "/api/v1/alerts_notification": { "get": { - "tags": ["CodeGate API", "Workspaces"], - "summary": "List Workspaces By Provider", - "description": "List workspaces by provider ID.", - "operationId": "v1_list_workspaces_by_provider", + "tags": ["CodeGate API", "Dashboard"], + "summary": "Stream Sse", + "description": "Send alerts event", + "operationId": "v1_stream_sse", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/v1/version": { + "get": { + "tags": ["CodeGate API", "Dashboard"], + "summary": "Version Check", + "operationId": "v1_version_check", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/v1/workspaces/{workspace_name}/token-usage": { + "get": { + "tags": ["CodeGate API", "Workspaces", "Token Usage"], + "summary": "Get Workspace Token Usage", + "description": "Get the token usage of a workspace.", + "operationId": "v1_get_workspace_token_usage", "parameters": [ { - "name": "provider_id", + "name": "workspace_name", "in": "path", "required": true, "schema": { "type": "string", - "format": "uuid", - "title": "Provider Id" + "title": "Workspace Name" } } ], @@ -989,11 +1132,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WorkspaceWithModel" - }, - "title": "Response V1 List Workspaces By Provider" + "$ref": "#/components/schemas/TokenUsageAggregate" } } } @@ -1011,65 +1150,141 @@ } } }, - "/api/v1/alerts_notification": { + "/api/v1/personas": { "get": { - "tags": ["CodeGate API", "Dashboard"], - "summary": "Stream Sse", - "description": "Send alerts event", - "operationId": "v1_stream_sse", + "tags": ["CodeGate API", "Personas"], + "summary": "List Personas", + "description": "List all personas.", + "operationId": "v1_list_personas", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { - "schema": {} + "schema": { + "items": { + "$ref": "#/components/schemas/Persona" + }, + "type": "array", + "title": "Response V1 List Personas" + } + } + } + } + } + }, + "post": { + "tags": ["CodeGate API", "Personas"], + "summary": "Create Persona", + "description": "Create a new persona.", + "operationId": "v1_create_persona", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PersonaRequest" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Persona" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } } } } } } }, - "/api/v1/version": { + "/api/v1/personas/{persona_name}": { "get": { - "tags": ["CodeGate API", "Dashboard"], - "summary": "Version Check", - "operationId": "v1_version_check", + "tags": ["CodeGate API", "Personas"], + "summary": "Get Persona", + "description": "Get a persona by name.", + "operationId": "v1_get_persona", + "parameters": [ + { + "name": "persona_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Persona Name" + } + } + ], "responses": { "200": { "description": "Successful Response", "content": { "application/json": { - "schema": {} + "schema": { + "$ref": "#/components/schemas/Persona" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } } } } } - } - }, - "/api/v1/workspaces/{workspace_name}/token-usage": { - "get": { - "tags": ["CodeGate API", "Workspaces", "Token Usage"], - "summary": "Get Workspace Token Usage", - "description": "Get the token usage of a workspace.", - "operationId": "v1_get_workspace_token_usage", + }, + "put": { + "tags": ["CodeGate API", "Personas"], + "summary": "Update Persona", + "description": "Update an existing persona.", + "operationId": "v1_update_persona", "parameters": [ { - "name": "workspace_name", + "name": "persona_name", "in": "path", "required": true, "schema": { "type": "string", - "title": "Workspace Name" + "title": "Persona Name" } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PersonaUpdateRequest" + } + } + } + }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TokenUsageAggregate" + "$ref": "#/components/schemas/Persona" } } } @@ -1085,6 +1300,38 @@ } } } + }, + "delete": { + "tags": ["CodeGate API", "Personas"], + "summary": "Delete Persona", + "description": "Delete a persona.", + "operationId": "v1_delete_persona", + "parameters": [ + { + "name": "persona_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Persona Name" + } + } + ], + "responses": { + "204": { + "description": "Successful Response" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } } } }, @@ -1303,6 +1550,26 @@ "enum": ["info", "critical"], "title": "AlertSeverity" }, + "AlertSummary": { + "properties": { + "malicious_packages": { + "type": "integer", + "title": "Malicious Packages" + }, + "pii": { + "type": "integer", + "title": "Pii" + }, + "secrets": { + "type": "integer", + "title": "Secrets" + } + }, + "type": "object", + "required": ["malicious_packages", "pii", "secrets"], + "title": "AlertSummary", + "description": "Represents a set of summary alerts" + }, "ChatMessage": { "properties": { "message": { @@ -1628,6 +1895,58 @@ "title": "MuxRule", "description": "Represents a mux rule for a provider." }, + "Persona": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + } + }, + "type": "object", + "required": ["id", "name", "description"], + "title": "Persona", + "description": "Represents a persona object." + }, + "PersonaRequest": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + } + }, + "type": "object", + "required": ["name", "description"], + "title": "PersonaRequest", + "description": "Model for creating a new Persona." + }, + "PersonaUpdateRequest": { + "properties": { + "new_name": { + "type": "string", + "title": "New Name" + }, + "new_description": { + "type": "string", + "title": "New Description" + } + }, + "type": "object", + "required": ["new_name", "new_description"], + "title": "PersonaUpdateRequest", + "description": "Model for updating a Persona." + }, "ProviderAuthType": { "type": "string", "enum": ["none", "passthrough", "api_key"], @@ -1857,27 +2176,6 @@ "type": "object", "required": ["custom_instructions", "muxing_rules"], "title": "WorkspaceConfig" - }, - "WorkspaceWithModel": { - "properties": { - "id": { - "type": "string", - "title": "Id" - }, - "name": { - "type": "string", - "pattern": "^[a-zA-Z0-9_-]+$", - "title": "Name" - }, - "provider_model_name": { - "type": "string", - "title": "Provider Model Name" - } - }, - "type": "object", - "required": ["id", "name", "provider_model_name"], - "title": "WorkspaceWithModel", - "description": "Returns a workspace ID with model name" } } } diff --git a/src/context/confirm-context.tsx b/src/context/confirm-context.tsx index 8509ce62..34c30e25 100644 --- a/src/context/confirm-context.tsx +++ b/src/context/confirm-context.tsx @@ -57,8 +57,12 @@ export function ConfirmProvider({ children }: { children: ReactNode }) { {children} - - + + {activeQuestion?.config.title} @@ -70,6 +74,7 @@ export function ConfirmProvider({ children }: { children: ReactNode }) { {activeQuestion?.config.buttons.no ?? ' '} + ) +} diff --git a/src/features/workspace/components/workspace-models-dropdown.tsx b/src/features/workspace/components/workspace-models-dropdown.tsx index 23c8c71e..b9c74db3 100644 --- a/src/features/workspace/components/workspace-models-dropdown.tsx +++ b/src/features/workspace/components/workspace-models-dropdown.tsx @@ -1,6 +1,5 @@ import { ModelByProvider, - MuxRule, V1ListAllModelsForAllProvidersResponse, } from '@/api/generated' import { @@ -16,17 +15,21 @@ import { import { ChevronDown, SearchMd } from '@untitled-ui/icons-react' import { map, groupBy } from 'lodash' import { useState } from 'react' +import { deserializeMuxModel, serializeMuxModel } from '../lib/mux-model-serde' +import { PreferredMuxRule } from '../hooks/use-muxing-rules-form-workspace' type Props = { - rule: MuxRule & { id: string } + rule: PreferredMuxRule isArchived: boolean models: V1ListAllModelsForAllProvidersResponse onChange: ({ model, - provider_id, + provider_name, + provider_type, }: { model: string - provider_id: string + provider_name: string + provider_type: string }) => void } @@ -37,7 +40,7 @@ function groupModelsByProviderName( id: providerName, textValue: providerName, items: items.map((item) => ({ - id: `${item.provider_id}@${item.name}`, + id: serializeMuxModel(item), textValue: item.name, })), })) @@ -77,12 +80,20 @@ export function WorkspaceModelsDropdown({ const [isOpen, setIsOpen] = useState(false) const [searchItem, setSearchItem] = useState('') const groupedModels = groupModelsByProviderName(models) - const currentProvider = models.find((p) => p.provider_id === rule.provider_id) + const currentProvider = models.find( + (p) => p.provider_name === rule.provider_name + ) const currentModel = currentProvider && rule.model ? `${currentProvider?.provider_name}/${rule.model}` : '' - const selectedKey = `${rule.provider_id}/${rule.model}` + const selectedKey = rule.provider_type + ? serializeMuxModel({ + name: rule.model, + provider_name: rule.provider_name, + provider_type: rule.provider_type, + }) + : undefined return (
@@ -110,19 +121,14 @@ export function WorkspaceModelsDropdown({ selectionBehavior="replace" selectedKeys={selectedKey ? [selectedKey] : []} onSelectionChange={(v) => { - if (v === 'all') { - return - } - const selectedValue = v.values().next().value - if (!selectedValue && typeof selectedValue !== 'string') return - if (typeof selectedValue === 'string') { - const atIndex = selectedValue.indexOf('@') - const provider_id = selectedValue.slice(0, atIndex) - const modelName = selectedValue.slice(atIndex + 1) - if (atIndex === -1 && (!provider_id || !modelName)) return + if (v === 'all') return + const id = v.values().next().value?.toString() + if (typeof id === 'string') { + const model = deserializeMuxModel(id) onChange({ - model: modelName, - provider_id, + model: model.name, + provider_name: model.provider_name, + provider_type: model.provider_type, }) setIsOpen(false) } diff --git a/src/features/workspace/components/workspace-muxing-model.tsx b/src/features/workspace/components/workspace-muxing-model.tsx index feb800cf..d6dcfcf5 100644 --- a/src/features/workspace/components/workspace-muxing-model.tsx +++ b/src/features/workspace/components/workspace-muxing-model.tsx @@ -19,7 +19,10 @@ import { } from '@stacklok/ui-kit' import { twMerge } from 'tailwind-merge' import { useMutationPreferredModelWorkspace } from '../hooks/use-mutation-preferred-model-workspace' -import { V1ListAllModelsForAllProvidersResponse } from '@/api/generated' +import { + ProviderType, + V1ListAllModelsForAllProvidersResponse, +} from '@/api/generated' import { FormEvent } from 'react' import { LayersThree01, @@ -37,6 +40,7 @@ import { } from '../hooks/use-muxing-rules-form-workspace' import { FormButtons } from '@/components/FormButtons' import { getRuleData, isRequestType } from '../lib/utils' +import { z } from 'zod' function MissingProviderBanner() { return ( @@ -120,9 +124,15 @@ function SortableItem({ rule={rule} isArchived={isArchived} models={models} - onChange={({ model, provider_id }) => - setRuleItem({ ...rule, provider_id, model }) - } + onChange={({ model, provider_name, provider_type }) => { + if (provider_type === undefined) return + setRuleItem({ + ...rule, + provider_name, + provider_type: z.nativeEnum(ProviderType).parse(provider_type), + model, + }) + }} /> {showRemoveButton && !isDefaultRule ? ( + ) +} diff --git a/src/features/workspace/hooks/use-muxing-rules-form-workspace.ts b/src/features/workspace/hooks/use-muxing-rules-form-workspace.ts index 2949c31a..5ee0e0c8 100644 --- a/src/features/workspace/hooks/use-muxing-rules-form-workspace.ts +++ b/src/features/workspace/hooks/use-muxing-rules-form-workspace.ts @@ -1,10 +1,12 @@ -import { MuxMatcherType, MuxRule } from '@/api/generated' +import { MuxMatcherType, MuxRule, ProviderType } from '@/api/generated' import { useFormState } from '@/hooks/useFormState' import { isEqual } from 'lodash' import { useCallback, useEffect, useRef } from 'react' import { v4 as uuidv4 } from 'uuid' -export type PreferredMuxRule = MuxRule & { id: string } +export type PreferredMuxRule = Omit & { + provider_type: ProviderType | undefined +} & { id: string } type MuxingRulesFormState = { rules: PreferredMuxRule[] @@ -12,7 +14,8 @@ type MuxingRulesFormState = { const DEFAULT_STATE: PreferredMuxRule = { id: uuidv4(), - provider_id: '', + provider_name: '', + provider_type: undefined, model: '', matcher: '', matcher_type: MuxMatcherType.CATCH_ALL, diff --git a/src/features/workspace/hooks/use-query-get-workspace-by-name.ts b/src/features/workspace/hooks/use-query-get-workspace-by-name.ts new file mode 100644 index 00000000..7aeb0a24 --- /dev/null +++ b/src/features/workspace/hooks/use-query-get-workspace-by-name.ts @@ -0,0 +1,12 @@ +import { v1GetWorkspaceByNameOptions } from '@/api/generated/@tanstack/react-query.gen' +import { useQuery } from '@tanstack/react-query' + +export function useQueryGetWorkspaceByName(options: { + path: { + workspace_name: string + } +}) { + return useQuery({ + ...v1GetWorkspaceByNameOptions(options), + }) +} diff --git a/src/features/workspace/lib/__tests__/mux-model-serde.test.ts b/src/features/workspace/lib/__tests__/mux-model-serde.test.ts new file mode 100644 index 00000000..49fdbe90 --- /dev/null +++ b/src/features/workspace/lib/__tests__/mux-model-serde.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect } from 'vitest' +import { serializeMuxModel, deserializeMuxModel } from '../mux-model-serde' +import { ModelByProvider } from '@/api/generated' + +describe('mux-model serialization/deserialization', () => { + it.each([ + { + name: 'deepseek-r1:1.5b', + provider_type: 'ollama', + provider_name: 'ollama', + }, + { + name: 'mistral-nemo:latest', + provider_type: 'ollama', + provider_name: 'ollama_muxing', + }, + { + name: '01-ai/yi-large', + provider_type: 'openrouter', + provider_name: 'openrouter_muxing', + }, + { + name: 'anthropic/claude-3-opus:beta', + provider_type: 'openrouter', + provider_name: 'openrouter_muxing', + }, + ] as ModelByProvider[])( + 'should correctly serialize and deserialize model: $name', + (model) => { + const serialized = serializeMuxModel(model) + const deserialized = deserializeMuxModel(serialized) + expect(deserialized).toEqual(model) + } + ) +}) diff --git a/src/features/workspace/lib/mux-model-serde.ts b/src/features/workspace/lib/mux-model-serde.ts new file mode 100644 index 00000000..fc0b7f63 --- /dev/null +++ b/src/features/workspace/lib/mux-model-serde.ts @@ -0,0 +1,17 @@ +import { ModelByProvider, ProviderType } from '@/api/generated' +import { z } from 'zod' + +export function serializeMuxModel(model: ModelByProvider): string { + return `${model.provider_name}___${model.provider_type}___${model.name}` +} + +export function deserializeMuxModel(str: string): ModelByProvider { + const [provider_name, provider_type, name] = str.split('___') + if (!provider_name || !provider_type || !name) + throw new Error('Invalid model') + return { + provider_name, + provider_type: z.nativeEnum(ProviderType).parse(provider_type), + name, + } +} diff --git a/src/mocks/msw/fixtures/GET_PROVIDERS_MODELS.json b/src/mocks/msw/fixtures/GET_PROVIDERS_MODELS.json index 7be5f209..2a273c8a 100644 --- a/src/mocks/msw/fixtures/GET_PROVIDERS_MODELS.json +++ b/src/mocks/msw/fixtures/GET_PROVIDERS_MODELS.json @@ -1,27 +1,27 @@ [ { "name": "claude-3.5", - "provider_id": "id_1", + "provider_type": "openrouter", "provider_name": "anthropic" }, { "name": "claude-3.6", - "provider_id": "id_2", + "provider_type": "openrouter", "provider_name": "anthropic" }, { "name": "claude-3.7", - "provider_id": "id_3", + "provider_type": "openrouter", "provider_name": "anthropic" }, { "name": "chatgpt-4o", - "provider_id": "id_4", + "provider_type": "openrouter", "provider_name": "openai" }, { "name": "chatgpt-4p", - "provider_id": "id_5", + "provider_type": "openrouter", "provider_name": "openai" } ] diff --git a/src/routes/route-provider-update.tsx b/src/routes/route-provider-update.tsx index c286e0f5..f4bff2f5 100644 --- a/src/routes/route-provider-update.tsx +++ b/src/routes/route-provider-update.tsx @@ -7,11 +7,11 @@ import { DialogContent, Form } from '@stacklok/ui-kit' import { useParams } from 'react-router-dom' export function RouteProviderUpdate() { - const { id } = useParams() - if (id === undefined) { - throw new Error('Provider id is required') + const { name } = useParams() + if (name === undefined) { + throw new Error('Provider name is required') } - const { setProvider, provider } = useProvider(id) + const { setProvider, provider } = useProvider(name) const { mutateAsync } = useMutationUpdateProvider() const handleSubmit = (event: React.FormEvent) => { diff --git a/src/routes/route-workspace.tsx b/src/routes/route-workspace.tsx index d5df90ed..6453768f 100644 --- a/src/routes/route-workspace.tsx +++ b/src/routes/route-workspace.tsx @@ -10,6 +10,7 @@ import { WorkspaceCustomInstructions } from '@/features/workspace/components/wor import { WorkspaceMuxingModel } from '@/features/workspace/components/workspace-muxing-model' import { PageContainer } from '@/components/page-container' import { WorkspaceActivateButton } from '@/features/workspace/components/workspace-activate-button' +import { WorkspaceDownloadButton } from '@/features/workspace/components/workspace-download-button' function WorkspaceArchivedBanner({ name }: { name: string }) { const restoreButtonProps = useRestoreWorkspaceButton({ workspaceName: name }) @@ -46,7 +47,13 @@ export function RouteWorkspace() { - +
+ + +
{isArchived ? : null} diff --git a/src/routes/route-workspaces.tsx b/src/routes/route-workspaces.tsx index b4f21b7d..e1eaf656 100644 --- a/src/routes/route-workspaces.tsx +++ b/src/routes/route-workspaces.tsx @@ -14,6 +14,7 @@ import { useNavigate } from 'react-router-dom' import { hrefs } from '@/lib/hrefs' import { PlusSquare } from '@untitled-ui/icons-react' import { PageContainer } from '@/components/page-container' +import { WorkspaceUploadButton } from '@/features/workspace/components/workspace-upload-button' export function RouteWorkspaces() { const navigate = useNavigate() @@ -28,15 +29,18 @@ export function RouteWorkspaces() { - - - Create - - - Create a new workspace - C - - +
+ + + + Create + + + Create a new workspace + C + + +
From 2a7a11927c18459620f9d90c07cdb7374b688a4f Mon Sep 17 00:00:00 2001 From: alex-mcgovern Date: Fri, 7 Mar 2025 10:42:34 +0000 Subject: [PATCH 3/6] fix integration bugs --- .../generated/@tanstack/react-query.gen.ts | 44 ++++----- src/api/generated/sdk.gen.ts | 55 +++++------ src/api/generated/types.gen.ts | 30 +++--- src/api/openapi.json | 98 +++++++++---------- .../providers/components/table-providers.tsx | 4 +- .../hooks/use-invalidate-providers-queries.ts | 2 + .../hooks/use-mutation-update-provider.ts | 10 +- .../hooks/use-query-muxing-rules-workspace.ts | 24 +++-- ...query-list-all-models-for-all-providers.ts | 4 + src/mocks/msw/handlers.ts | 16 ++- src/routes/route-provider-update.tsx | 2 +- src/routes/route-workspaces.tsx | 4 +- 12 files changed, 153 insertions(+), 140 deletions(-) diff --git a/src/api/generated/@tanstack/react-query.gen.ts b/src/api/generated/@tanstack/react-query.gen.ts index 79ebace2..1098cb0d 100644 --- a/src/api/generated/@tanstack/react-query.gen.ts +++ b/src/api/generated/@tanstack/react-query.gen.ts @@ -10,9 +10,9 @@ import { v1ListAllModelsForAllProviders, v1ListModelsByProvider, v1GetProviderEndpoint, - v1ConfigureAuthMaterial, v1UpdateProviderEndpoint, v1DeleteProviderEndpoint, + v1ConfigureAuthMaterial, v1ListWorkspaces, v1CreateWorkspace, v1ListActiveWorkspaces, @@ -47,15 +47,15 @@ import type { V1AddProviderEndpointResponse, V1ListModelsByProviderData, V1GetProviderEndpointData, - V1ConfigureAuthMaterialData, - V1ConfigureAuthMaterialError, - V1ConfigureAuthMaterialResponse, V1UpdateProviderEndpointData, V1UpdateProviderEndpointError, V1UpdateProviderEndpointResponse, V1DeleteProviderEndpointData, V1DeleteProviderEndpointError, V1DeleteProviderEndpointResponse, + V1ConfigureAuthMaterialData, + V1ConfigureAuthMaterialError, + V1ConfigureAuthMaterialResponse, V1ListWorkspacesData, V1CreateWorkspaceData, V1CreateWorkspaceError, @@ -281,16 +281,16 @@ export const v1GetProviderEndpointOptions = ( }) } -export const v1ConfigureAuthMaterialMutation = ( - options?: Partial> +export const v1UpdateProviderEndpointMutation = ( + options?: Partial> ) => { const mutationOptions: UseMutationOptions< - V1ConfigureAuthMaterialResponse, - V1ConfigureAuthMaterialError, - OptionsLegacyParser + V1UpdateProviderEndpointResponse, + V1UpdateProviderEndpointError, + OptionsLegacyParser > = { mutationFn: async (localOptions) => { - const { data } = await v1ConfigureAuthMaterial({ + const { data } = await v1UpdateProviderEndpoint({ ...options, ...localOptions, throwOnError: true, @@ -301,16 +301,16 @@ export const v1ConfigureAuthMaterialMutation = ( return mutationOptions } -export const v1UpdateProviderEndpointMutation = ( - options?: Partial> +export const v1DeleteProviderEndpointMutation = ( + options?: Partial> ) => { const mutationOptions: UseMutationOptions< - V1UpdateProviderEndpointResponse, - V1UpdateProviderEndpointError, - OptionsLegacyParser + V1DeleteProviderEndpointResponse, + V1DeleteProviderEndpointError, + OptionsLegacyParser > = { mutationFn: async (localOptions) => { - const { data } = await v1UpdateProviderEndpoint({ + const { data } = await v1DeleteProviderEndpoint({ ...options, ...localOptions, throwOnError: true, @@ -321,16 +321,16 @@ export const v1UpdateProviderEndpointMutation = ( return mutationOptions } -export const v1DeleteProviderEndpointMutation = ( - options?: Partial> +export const v1ConfigureAuthMaterialMutation = ( + options?: Partial> ) => { const mutationOptions: UseMutationOptions< - V1DeleteProviderEndpointResponse, - V1DeleteProviderEndpointError, - OptionsLegacyParser + V1ConfigureAuthMaterialResponse, + V1ConfigureAuthMaterialError, + OptionsLegacyParser > = { mutationFn: async (localOptions) => { - const { data } = await v1DeleteProviderEndpoint({ + const { data } = await v1ConfigureAuthMaterial({ ...options, ...localOptions, throwOnError: true, diff --git a/src/api/generated/sdk.gen.ts b/src/api/generated/sdk.gen.ts index 16f2fbad..1a3bab9e 100644 --- a/src/api/generated/sdk.gen.ts +++ b/src/api/generated/sdk.gen.ts @@ -22,15 +22,15 @@ import type { V1GetProviderEndpointData, V1GetProviderEndpointError, V1GetProviderEndpointResponse, - V1ConfigureAuthMaterialData, - V1ConfigureAuthMaterialError, - V1ConfigureAuthMaterialResponse, V1UpdateProviderEndpointData, V1UpdateProviderEndpointError, V1UpdateProviderEndpointResponse, V1DeleteProviderEndpointData, V1DeleteProviderEndpointError, V1DeleteProviderEndpointResponse, + V1ConfigureAuthMaterialData, + V1ConfigureAuthMaterialError, + V1ConfigureAuthMaterialResponse, V1ListWorkspacesData, V1ListWorkspacesError, V1ListWorkspacesResponse, @@ -190,7 +190,7 @@ export const v1ListModelsByProvider = ( ThrowOnError >({ ...options, - url: '/api/v1/provider-endpoints/{provider_id}/models', + url: '/api/v1/provider-endpoints/{provider_name}/models', }) } @@ -211,26 +211,9 @@ export const v1GetProviderEndpoint = ( }) } -/** - * Configure Auth Material - * Configure auth material for a provider. - */ -export const v1ConfigureAuthMaterial = ( - options: OptionsLegacyParser -) => { - return (options?.client ?? client).put< - V1ConfigureAuthMaterialResponse, - V1ConfigureAuthMaterialError, - ThrowOnError - >({ - ...options, - url: '/api/v1/provider-endpoints/{provider_id}/auth-material', - }) -} - /** * Update Provider Endpoint - * Update a provider endpoint by ID. + * Update a provider endpoint by name. */ export const v1UpdateProviderEndpoint = ( options: OptionsLegacyParser @@ -241,13 +224,13 @@ export const v1UpdateProviderEndpoint = ( ThrowOnError >({ ...options, - url: '/api/v1/provider-endpoints/{provider_id}', + url: '/api/v1/provider-endpoints/{provider_name}', }) } /** * Delete Provider Endpoint - * Delete a provider endpoint by id. + * Delete a provider endpoint by name. */ export const v1DeleteProviderEndpoint = ( options: OptionsLegacyParser @@ -258,7 +241,24 @@ export const v1DeleteProviderEndpoint = ( ThrowOnError >({ ...options, - url: '/api/v1/provider-endpoints/{provider_id}', + url: '/api/v1/provider-endpoints/{provider_name}', + }) +} + +/** + * Configure Auth Material + * Configure auth material for a provider. + */ +export const v1ConfigureAuthMaterial = ( + options: OptionsLegacyParser +) => { + return (options?.client ?? client).put< + V1ConfigureAuthMaterialResponse, + V1ConfigureAuthMaterialError, + ThrowOnError + >({ + ...options, + url: '/api/v1/provider-endpoints/{provider_name}/auth-material', }) } @@ -267,10 +267,9 @@ export const v1DeleteProviderEndpoint = ( * List all workspaces. * * Args: - * provider_id (Optional[UUID]): Filter workspaces by provider ID. If provided, + * provider_name (Optional[str]): Filter workspaces by provider name. If provided, * will return workspaces where models from the specified provider (e.g., OpenAI, - * Anthropic) have been used in workspace muxing rules. Note that you must - * refer to a provider by ID, not by name. + * Anthropic) have been used in workspace muxing rules. * * Returns: * ListWorkspacesResponse: A response object containing the list of workspaces. diff --git a/src/api/generated/types.gen.ts b/src/api/generated/types.gen.ts index 98055318..1cde1cef 100644 --- a/src/api/generated/types.gen.ts +++ b/src/api/generated/types.gen.ts @@ -341,7 +341,7 @@ export type V1ListAllModelsForAllProvidersError = unknown export type V1ListModelsByProviderData = { path: { - provider_id: string + provider_name: string } } @@ -359,21 +359,10 @@ export type V1GetProviderEndpointResponse = ProviderEndpoint export type V1GetProviderEndpointError = HTTPValidationError -export type V1ConfigureAuthMaterialData = { - body: ConfigureAuthMaterial - path: { - provider_id: string - } -} - -export type V1ConfigureAuthMaterialResponse = void - -export type V1ConfigureAuthMaterialError = HTTPValidationError - export type V1UpdateProviderEndpointData = { body: ProviderEndpoint path: { - provider_id: string + provider_name: string } } @@ -383,7 +372,7 @@ export type V1UpdateProviderEndpointError = HTTPValidationError export type V1DeleteProviderEndpointData = { path: { - provider_id: string + provider_name: string } } @@ -391,9 +380,20 @@ export type V1DeleteProviderEndpointResponse = unknown export type V1DeleteProviderEndpointError = HTTPValidationError +export type V1ConfigureAuthMaterialData = { + body: ConfigureAuthMaterial + path: { + provider_name: string + } +} + +export type V1ConfigureAuthMaterialResponse = void + +export type V1ConfigureAuthMaterialError = HTTPValidationError + export type V1ListWorkspacesData = { query?: { - provider_id?: string | null + provider_name?: string | null } } diff --git a/src/api/openapi.json b/src/api/openapi.json index f37eb299..895b2983 100644 --- a/src/api/openapi.json +++ b/src/api/openapi.json @@ -137,7 +137,7 @@ } } }, - "/api/v1/provider-endpoints/{provider_id}/models": { + "/api/v1/provider-endpoints/{provider_name}/models": { "get": { "tags": ["CodeGate API", "Providers"], "summary": "List Models By Provider", @@ -145,13 +145,12 @@ "operationId": "v1_list_models_by_provider", "parameters": [ { - "name": "provider_id", + "name": "provider_name", "in": "path", "required": true, "schema": { "type": "string", - "format": "uuid", - "title": "Provider Id" + "title": "Provider Name" } } ], @@ -223,11 +222,11 @@ } } }, - "delete": { + "put": { "tags": ["CodeGate API", "Providers"], - "summary": "Delete Provider Endpoint", - "description": "Delete a provider endpoint by id.", - "operationId": "v1_delete_provider_endpoint", + "summary": "Update Provider Endpoint", + "description": "Update a provider endpoint by name.", + "operationId": "v1_update_provider_endpoint", "parameters": [ { "name": "provider_name", @@ -239,12 +238,24 @@ } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProviderEndpoint" + } + } + } + }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { - "schema": {} + "schema": { + "$ref": "#/components/schemas/ProviderEndpoint" + } } } }, @@ -259,39 +270,31 @@ } } } - } - }, - "/api/v1/provider-endpoints/{provider_id}/auth-material": { - "put": { + }, + "delete": { "tags": ["CodeGate API", "Providers"], - "summary": "Configure Auth Material", - "description": "Configure auth material for a provider.", - "operationId": "v1_configure_auth_material", + "summary": "Delete Provider Endpoint", + "description": "Delete a provider endpoint by name.", + "operationId": "v1_delete_provider_endpoint", "parameters": [ { - "name": "provider_id", + "name": "provider_name", "in": "path", "required": true, "schema": { "type": "string", - "format": "uuid", - "title": "Provider Id" + "title": "Provider Name" } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ConfigureAuthMaterial" + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} } } - } - }, - "responses": { - "204": { - "description": "Successful Response" }, "422": { "description": "Validation Error", @@ -306,21 +309,20 @@ } } }, - "/api/v1/provider-endpoints/{provider_id}": { + "/api/v1/provider-endpoints/{provider_name}/auth-material": { "put": { "tags": ["CodeGate API", "Providers"], - "summary": "Update Provider Endpoint", - "description": "Update a provider endpoint by ID.", - "operationId": "v1_update_provider_endpoint", + "summary": "Configure Auth Material", + "description": "Configure auth material for a provider.", + "operationId": "v1_configure_auth_material", "parameters": [ { - "name": "provider_id", + "name": "provider_name", "in": "path", "required": true, "schema": { "type": "string", - "format": "uuid", - "title": "Provider Id" + "title": "Provider Name" } } ], @@ -329,21 +331,14 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProviderEndpoint" + "$ref": "#/components/schemas/ConfigureAuthMaterial" } } } }, "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProviderEndpoint" - } - } - } + "204": { + "description": "Successful Response" }, "422": { "description": "Validation Error", @@ -362,24 +357,23 @@ "get": { "tags": ["CodeGate API", "Workspaces"], "summary": "List Workspaces", - "description": "List all workspaces.\n\nArgs:\n provider_id (Optional[UUID]): Filter workspaces by provider ID. If provided,\n will return workspaces where models from the specified provider (e.g., OpenAI,\n Anthropic) have been used in workspace muxing rules. Note that you must\n refer to a provider by ID, not by name.\n\nReturns:\n ListWorkspacesResponse: A response object containing the list of workspaces.", + "description": "List all workspaces.\n\nArgs:\n provider_name (Optional[str]): Filter workspaces by provider name. If provided,\n will return workspaces where models from the specified provider (e.g., OpenAI,\n Anthropic) have been used in workspace muxing rules.\n\nReturns:\n ListWorkspacesResponse: A response object containing the list of workspaces.", "operationId": "v1_list_workspaces", "parameters": [ { - "name": "provider_id", + "name": "provider_name", "in": "query", "required": false, "schema": { "anyOf": [ { - "type": "string", - "format": "uuid" + "type": "string" }, { "type": "null" } ], - "title": "Provider Id" + "title": "Provider Name" } } ], diff --git a/src/features/providers/components/table-providers.tsx b/src/features/providers/components/table-providers.tsx index 625ef4a8..74573b95 100644 --- a/src/features/providers/components/table-providers.tsx +++ b/src/features/providers/components/table-providers.tsx @@ -55,7 +55,7 @@ function CellRenderer({ column: Column row: ProviderEndpoint }) { - const deleteProvider = useConfirmDeleteProvider(row.id) + const deleteProvider = useConfirmDeleteProvider(row.name) return match(column.id) .with(COLUMN_MAP.provider, () => ( @@ -95,7 +95,7 @@ function CellRenderer({ variant="tertiary" onPress={() => deleteProvider({ - path: { provider_id: row.id as string }, + path: { provider_name: row.name as string }, }) } > diff --git a/src/features/providers/hooks/use-invalidate-providers-queries.ts b/src/features/providers/hooks/use-invalidate-providers-queries.ts index 5dc3400f..2c9f5c41 100644 --- a/src/features/providers/hooks/use-invalidate-providers-queries.ts +++ b/src/features/providers/hooks/use-invalidate-providers-queries.ts @@ -1,4 +1,5 @@ import { + v1GetWorkspaceMuxesQueryKey, v1ListAllModelsForAllProvidersQueryKey, v1ListProviderEndpointsQueryKey, v1ListWorkspacesQueryKey, @@ -15,6 +16,7 @@ export function useInvalidateProvidersQueries() { v1ListProviderEndpointsQueryKey, v1ListAllModelsForAllProvidersQueryKey, v1ListWorkspacesQueryKey, + v1GetWorkspaceMuxesQueryKey, ]) }, [queryClient]) diff --git a/src/features/providers/hooks/use-mutation-update-provider.ts b/src/features/providers/hooks/use-mutation-update-provider.ts index 1aba8fbe..97a6e9e1 100644 --- a/src/features/providers/hooks/use-mutation-update-provider.ts +++ b/src/features/providers/hooks/use-mutation-update-provider.ts @@ -14,13 +14,11 @@ export function useMutationUpdateProvider() { const mutationFn = async ({ api_key, + oldName, ...rest - }: AddProviderEndpointRequest) => { - const provider_id = rest.id - if (!provider_id) throw new Error('Provider is missing') - + }: AddProviderEndpointRequest & { oldName: string }) => { const updateProviderPromise = v1UpdateProviderEndpoint({ - path: { provider_id }, + path: { provider_name: oldName }, body: rest, }) @@ -30,7 +28,7 @@ export function useMutationUpdateProvider() { } const updateApiKey = v1ConfigureAuthMaterial({ - path: { provider_id }, + path: { provider_name: oldName }, body: { api_key: rest.auth_type === ProviderAuthType.API_KEY ? api_key : null, auth_type: rest.auth_type as ProviderAuthType, diff --git a/src/features/workspace/hooks/use-query-muxing-rules-workspace.ts b/src/features/workspace/hooks/use-query-muxing-rules-workspace.ts index b42d0fba..c7c8551c 100644 --- a/src/features/workspace/hooks/use-query-muxing-rules-workspace.ts +++ b/src/features/workspace/hooks/use-query-muxing-rules-workspace.ts @@ -1,20 +1,24 @@ -import { V1GetWorkspaceMuxesData } from "@/api/generated"; -import { v1GetWorkspaceMuxesOptions } from "@/api/generated/@tanstack/react-query.gen"; -import { useQuery } from "@tanstack/react-query"; -import { useMemo } from "react"; +import { V1GetWorkspaceMuxesData } from '@/api/generated' +import { v1GetWorkspaceMuxesOptions } from '@/api/generated/@tanstack/react-query.gen' +import { getQueryCacheConfig } from '@/lib/react-query-utils' +import { useQuery } from '@tanstack/react-query' +import { useMemo } from 'react' export const useQueryMuxingRulesWorkspace = (workspace_name: string) => { const options: V1GetWorkspaceMuxesData & - Omit = useMemo( + Omit = useMemo( () => ({ path: { workspace_name }, }), - [workspace_name], - ); + [workspace_name] + ) const { data = [], ...rest } = useQuery({ ...v1GetWorkspaceMuxesOptions(options), - }); + ...getQueryCacheConfig('no-cache'), + // eslint-disable-next-line no-restricted-syntax + refetchOnMount: true, + }) - return { data: data, ...rest }; -}; + return { data: data, ...rest } +} diff --git a/src/hooks/use-query-list-all-models-for-all-providers.ts b/src/hooks/use-query-list-all-models-for-all-providers.ts index dd3d5995..b6f70a5f 100644 --- a/src/hooks/use-query-list-all-models-for-all-providers.ts +++ b/src/hooks/use-query-list-all-models-for-all-providers.ts @@ -1,8 +1,12 @@ import { useQuery } from '@tanstack/react-query' import { v1ListAllModelsForAllProvidersOptions } from '@/api/generated/@tanstack/react-query.gen' +import { getQueryCacheConfig } from '@/lib/react-query-utils' export const useQueryListAllModelsForAllProviders = () => { return useQuery({ ...v1ListAllModelsForAllProvidersOptions(), + ...getQueryCacheConfig('no-cache'), + // eslint-disable-next-line no-restricted-syntax + refetchOnMount: true, }) } diff --git a/src/mocks/msw/handlers.ts b/src/mocks/msw/handlers.ts index 5909edfc..93823967 100644 --- a/src/mocks/msw/handlers.ts +++ b/src/mocks/msw/handlers.ts @@ -70,6 +70,15 @@ export const handlers = [ { status: 201 } ) ), + http.get(mswEndpoint('/api/v1/workspaces/:workspace_name'), () => + HttpResponse.json({ + name: 'foo', + config: { + custom_instructions: '', + muxing_rules: [], + }, + }) + ), http.post( mswEndpoint('/api/v1/workspaces/archive/:workspace_name/recover'), () => new HttpResponse(null, { status: 204 }) @@ -124,13 +133,14 @@ export const handlers = [ mswEndpoint('/api/v1/workspaces/:workspace_name/muxes'), () => new HttpResponse(null, { status: 204 }) ), - http.get(mswEndpoint('/api/v1/provider-endpoints/:provider_id/models'), () => - HttpResponse.json(mockedProvidersModels) + http.get( + mswEndpoint('/api/v1/provider-endpoints/:provider_name/models'), + () => HttpResponse.json(mockedProvidersModels) ), http.get(mswEndpoint('/api/v1/provider-endpoints/models'), () => HttpResponse.json(mockedProvidersModels) ), - http.get(mswEndpoint('/api/v1/provider-endpoints/:provider_id'), () => + http.get(mswEndpoint('/api/v1/provider-endpoints/:provider_name'), () => HttpResponse.json(mockedProviders[0]) ), http.get(mswEndpoint('/api/v1/provider-endpoints'), () => diff --git a/src/routes/route-provider-update.tsx b/src/routes/route-provider-update.tsx index f4bff2f5..2f756c4f 100644 --- a/src/routes/route-provider-update.tsx +++ b/src/routes/route-provider-update.tsx @@ -16,7 +16,7 @@ export function RouteProviderUpdate() { const handleSubmit = (event: React.FormEvent) => { event.preventDefault() - mutateAsync(provider) + mutateAsync({ ...provider, oldName: name }) } // TODO add empty state and loading in a next step diff --git a/src/routes/route-workspaces.tsx b/src/routes/route-workspaces.tsx index e1eaf656..a5001364 100644 --- a/src/routes/route-workspaces.tsx +++ b/src/routes/route-workspaces.tsx @@ -36,7 +36,9 @@ export function RouteWorkspaces() { Create - Create a new workspace + + Create a new workspace + C From 5bd14e75b09b5f3e805517b399a8d051092288ad Mon Sep 17 00:00:00 2001 From: alex-mcgovern Date: Mon, 10 Mar 2025 08:54:57 +0000 Subject: [PATCH 4/6] fix cache invalidation bugs --- .../hooks/use-mutation-set-workspace-custom-instructions.tsx | 4 ++++ src/features/workspace/hooks/use-mutation-update-workspace.ts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/features/workspace/hooks/use-mutation-set-workspace-custom-instructions.tsx b/src/features/workspace/hooks/use-mutation-set-workspace-custom-instructions.tsx index af5f86e9..7c9bce0d 100644 --- a/src/features/workspace/hooks/use-mutation-set-workspace-custom-instructions.tsx +++ b/src/features/workspace/hooks/use-mutation-set-workspace-custom-instructions.tsx @@ -1,5 +1,7 @@ import { + v1GetWorkspaceByNameQueryKey, v1GetWorkspaceCustomInstructionsQueryKey, + v1GetWorkspaceMuxesQueryKey, v1SetWorkspaceCustomInstructionsMutation, } from '@/api/generated/@tanstack/react-query.gen' import { V1GetWorkspaceCustomInstructionsData } from '@/api/generated' @@ -16,7 +18,9 @@ export function useMutationSetWorkspaceCustomInstructions( ...v1SetWorkspaceCustomInstructionsMutation(options), onSuccess: () => invalidateQueries(queryClient, [ + v1GetWorkspaceMuxesQueryKey, v1GetWorkspaceCustomInstructionsQueryKey, + v1GetWorkspaceByNameQueryKey, ]), successMsg: 'Successfully updated custom instructions', }) diff --git a/src/features/workspace/hooks/use-mutation-update-workspace.ts b/src/features/workspace/hooks/use-mutation-update-workspace.ts index 5ae08b88..d168f7c2 100644 --- a/src/features/workspace/hooks/use-mutation-update-workspace.ts +++ b/src/features/workspace/hooks/use-mutation-update-workspace.ts @@ -1,4 +1,5 @@ import { + v1GetWorkspaceByNameQueryKey, v1GetWorkspaceCustomInstructionsQueryKey, v1GetWorkspaceMuxesQueryKey, v1UpdateWorkspaceMutation, @@ -20,6 +21,7 @@ export function useMutationUpdateWorkspace() { queryKeyFns: [ v1GetWorkspaceMuxesQueryKey, v1GetWorkspaceCustomInstructionsQueryKey, + v1GetWorkspaceByNameQueryKey, ], }) await invalidate() From dfbd5d2686326f8a8b10ce7307c51efff9406756 Mon Sep 17 00:00:00 2001 From: alex-mcgovern Date: Wed, 12 Mar 2025 09:10:40 +0000 Subject: [PATCH 5/6] Merge branch 'main' of github.com:stacklok/codegate-ui into feat/shareable-workspaces --- src/api/generated/types.gen.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/api/generated/types.gen.ts b/src/api/generated/types.gen.ts index 8647eb71..8dbeeb3c 100644 --- a/src/api/generated/types.gen.ts +++ b/src/api/generated/types.gen.ts @@ -239,31 +239,6 @@ export type PersonaUpdateRequest = { new_description: string } -/** - * Represents a persona object. - */ -export type Persona = { - id: string - name: string - description: string -} - -/** - * Model for creating a new Persona. - */ -export type PersonaRequest = { - name: string - description: string -} - -/** - * Model for updating a Persona. - */ -export type PersonaUpdateRequest = { - new_name: string - new_description: string -} - /** * Represents the different types of auth we support for providers. */ From 6b437670bdf483a03f33cf3ca6383c704c6380bc Mon Sep 17 00:00:00 2001 From: alex-mcgovern Date: Mon, 17 Mar 2025 10:28:08 +0000 Subject: [PATCH 6/6] fix: cache invalidation bug when updating mux config --- .../workspace/hooks/use-invalidate-workspace-queries.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/features/workspace/hooks/use-invalidate-workspace-queries.ts b/src/features/workspace/hooks/use-invalidate-workspace-queries.ts index 9925b9dd..dd131f42 100644 --- a/src/features/workspace/hooks/use-invalidate-workspace-queries.ts +++ b/src/features/workspace/hooks/use-invalidate-workspace-queries.ts @@ -1,4 +1,5 @@ import { + v1GetWorkspaceByNameQueryKey, v1GetWorkspaceMuxesQueryKey, v1ListArchivedWorkspacesQueryKey, v1ListWorkspacesQueryKey, @@ -15,6 +16,7 @@ export function useInvalidateWorkspaceQueries() { v1ListWorkspacesQueryKey, v1ListArchivedWorkspacesQueryKey, v1GetWorkspaceMuxesQueryKey, + v1GetWorkspaceByNameQueryKey, ]) }, [queryClient])