@shipfox/api-agent 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +30 -0
  3. package/dist/core/custom-model-provider-config-service.d.ts +4 -0
  4. package/dist/core/custom-model-provider-config-service.d.ts.map +1 -1
  5. package/dist/core/custom-model-provider-config-service.js +19 -16
  6. package/dist/core/custom-model-provider-config-service.js.map +1 -1
  7. package/dist/core/model-provider-config-service.d.ts +4 -0
  8. package/dist/core/model-provider-config-service.d.ts.map +1 -1
  9. package/dist/core/model-provider-config-service.js +10 -8
  10. package/dist/core/model-provider-config-service.js.map +1 -1
  11. package/dist/core/resolve-runtime-credentials.d.ts +6 -2
  12. package/dist/core/resolve-runtime-credentials.d.ts.map +1 -1
  13. package/dist/core/resolve-runtime-credentials.js +10 -5
  14. package/dist/core/resolve-runtime-credentials.js.map +1 -1
  15. package/dist/core/secrets-client.d.ts +4 -0
  16. package/dist/core/secrets-client.d.ts.map +1 -0
  17. package/dist/core/secrets-client.js +6 -0
  18. package/dist/core/secrets-client.js.map +1 -0
  19. package/dist/index.d.ts +4 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +19 -13
  22. package/dist/index.js.map +1 -1
  23. package/dist/presentation/e2eRoutes/create-model-provider.d.ts +2 -1
  24. package/dist/presentation/e2eRoutes/create-model-provider.d.ts.map +1 -1
  25. package/dist/presentation/e2eRoutes/create-model-provider.js +46 -45
  26. package/dist/presentation/e2eRoutes/create-model-provider.js.map +1 -1
  27. package/dist/presentation/e2eRoutes/index.d.ts +2 -0
  28. package/dist/presentation/e2eRoutes/index.d.ts.map +1 -1
  29. package/dist/presentation/e2eRoutes/index.js +9 -6
  30. package/dist/presentation/e2eRoutes/index.js.map +1 -1
  31. package/dist/presentation/index.d.ts +1 -1
  32. package/dist/presentation/index.d.ts.map +1 -1
  33. package/dist/presentation/index.js +1 -1
  34. package/dist/presentation/index.js.map +1 -1
  35. package/dist/presentation/inter-module.d.ts +7 -0
  36. package/dist/presentation/inter-module.d.ts.map +1 -0
  37. package/dist/presentation/inter-module.js +45 -0
  38. package/dist/presentation/inter-module.js.map +1 -0
  39. package/dist/presentation/routes/create-custom-model-provider.d.ts +2 -1
  40. package/dist/presentation/routes/create-custom-model-provider.d.ts.map +1 -1
  41. package/dist/presentation/routes/create-custom-model-provider.js +39 -35
  42. package/dist/presentation/routes/create-custom-model-provider.js.map +1 -1
  43. package/dist/presentation/routes/delete-model-provider-config.d.ts +2 -1
  44. package/dist/presentation/routes/delete-model-provider-config.d.ts.map +1 -1
  45. package/dist/presentation/routes/delete-model-provider-config.js +33 -29
  46. package/dist/presentation/routes/delete-model-provider-config.js.map +1 -1
  47. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.d.ts +2 -1
  48. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.d.ts.map +1 -1
  49. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.js +32 -28
  50. package/dist/presentation/routes/discover-custom-model-provider-models-by-slug.js.map +1 -1
  51. package/dist/presentation/routes/errors.d.ts.map +1 -1
  52. package/dist/presentation/routes/errors.js +13 -12
  53. package/dist/presentation/routes/errors.js.map +1 -1
  54. package/dist/presentation/routes/index.d.ts +2 -0
  55. package/dist/presentation/routes/index.d.ts.map +1 -1
  56. package/dist/presentation/routes/index.js +32 -29
  57. package/dist/presentation/routes/index.js.map +1 -1
  58. package/dist/presentation/routes/update-custom-model-provider.d.ts +2 -1
  59. package/dist/presentation/routes/update-custom-model-provider.d.ts.map +1 -1
  60. package/dist/presentation/routes/update-custom-model-provider.js +41 -37
  61. package/dist/presentation/routes/update-custom-model-provider.js.map +1 -1
  62. package/dist/presentation/routes/upsert-model-provider-config.d.ts +2 -1
  63. package/dist/presentation/routes/upsert-model-provider-config.d.ts.map +1 -1
  64. package/dist/presentation/routes/upsert-model-provider-config.js +46 -42
  65. package/dist/presentation/routes/upsert-model-provider-config.js.map +1 -1
  66. package/dist/tsconfig.test.tsbuildinfo +1 -1
  67. package/package.json +11 -8
  68. package/src/core/custom-model-provider-config-service.ts +50 -34
  69. package/src/core/model-provider-config-service.test.ts +1 -3
  70. package/src/core/model-provider-config-service.ts +18 -15
  71. package/src/core/resolve-runtime-credentials.test.ts +3 -3
  72. package/src/core/resolve-runtime-credentials.ts +22 -8
  73. package/src/core/secrets-client.ts +13 -0
  74. package/src/index.ts +13 -8
  75. package/src/presentation/e2eRoutes/create-model-provider.ts +41 -39
  76. package/src/presentation/e2eRoutes/index.ts +6 -4
  77. package/src/presentation/index.ts +1 -1
  78. package/src/presentation/inter-module.ts +78 -0
  79. package/src/presentation/routes/create-custom-model-provider.ts +36 -30
  80. package/src/presentation/routes/delete-model-provider-config.ts +25 -22
  81. package/src/presentation/routes/discover-custom-model-provider-models-by-slug.ts +31 -25
  82. package/src/presentation/routes/errors.ts +11 -11
  83. package/src/presentation/routes/index.ts +32 -27
  84. package/src/presentation/routes/model-provider-config.test.ts +1 -1
  85. package/src/presentation/routes/update-custom-model-provider.ts +40 -34
  86. package/src/presentation/routes/upsert-model-provider-config.ts +43 -37
  87. package/test/fixtures/secrets-client.ts +80 -0
  88. package/test/globalSetup.ts +0 -9
  89. package/test/setup.ts +8 -0
  90. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,78 @@
1
+ import {agentInterModuleContract} from '@shipfox/api-agent-dto/inter-module';
2
+ import {secretsInterModuleContract} from '@shipfox/api-secrets-dto/inter-module';
3
+ import {
4
+ createInterModuleKnownError,
5
+ defineInterModulePresentation,
6
+ type InterModulePresentation,
7
+ isInterModuleKnownError,
8
+ } from '@shipfox/inter-module';
9
+ import {
10
+ InvalidAgentModelError,
11
+ ModelProviderConfigNotFoundError,
12
+ UnsupportedHarnessProviderError,
13
+ UnsupportedHarnessThinkingError,
14
+ UnsupportedModelProviderError,
15
+ } from '#core/errors.js';
16
+ import {resolveAgentConfig} from '#core/resolve-agent-config.js';
17
+ import {resolveRuntimeCredentials} from '#core/resolve-runtime-credentials.js';
18
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
19
+ import {createWorkspaceAgentDefaultsResolver} from '#core/workspace-agent-defaults-resolver.js';
20
+
21
+ export function createAgentInterModulePresentation(params: {
22
+ secrets: AgentSecretsClient;
23
+ }): InterModulePresentation<typeof agentInterModuleContract> {
24
+ return defineInterModulePresentation(agentInterModuleContract, {
25
+ resolveAgentConfig: async ({workspaceId, config}) => {
26
+ try {
27
+ const resolve =
28
+ workspaceId === null
29
+ ? resolveAgentConfig
30
+ : await createWorkspaceAgentDefaultsResolver(workspaceId);
31
+ return await resolve(config);
32
+ } catch (error) {
33
+ throw toResolveAgentConfigKnownError(error);
34
+ }
35
+ },
36
+ resolveRuntimeCredentials: async (input) => {
37
+ try {
38
+ return await resolveRuntimeCredentials(input, {secrets: params.secrets});
39
+ } catch (error) {
40
+ throw toResolveRuntimeCredentialsKnownError(error);
41
+ }
42
+ },
43
+ });
44
+ }
45
+
46
+ function toResolveAgentConfigKnownError(error: unknown): unknown {
47
+ if (
48
+ error instanceof InvalidAgentModelError ||
49
+ error instanceof UnsupportedHarnessProviderError ||
50
+ error instanceof UnsupportedHarnessThinkingError ||
51
+ error instanceof UnsupportedModelProviderError
52
+ ) {
53
+ return createInterModuleKnownError(
54
+ agentInterModuleContract.methods.resolveAgentConfig,
55
+ 'agent-config-invalid',
56
+ {},
57
+ );
58
+ }
59
+ return error;
60
+ }
61
+
62
+ function toResolveRuntimeCredentialsKnownError(error: unknown): unknown {
63
+ if (error instanceof ModelProviderConfigNotFoundError) {
64
+ return createInterModuleKnownError(
65
+ agentInterModuleContract.methods.resolveRuntimeCredentials,
66
+ 'model-provider-not-configured',
67
+ {},
68
+ );
69
+ }
70
+ if (isInterModuleKnownError(secretsInterModuleContract.methods.getSecretsByNamespace, error)) {
71
+ return createInterModuleKnownError(
72
+ agentInterModuleContract.methods.resolveRuntimeCredentials,
73
+ 'model-provider-credentials-invalid',
74
+ {},
75
+ );
76
+ }
77
+ return error;
78
+ }
@@ -6,40 +6,46 @@ import {requireWorkspaceAccess} from '@shipfox/api-auth-context';
6
6
  import {defineRoute} from '@shipfox/node-fastify';
7
7
  import {z} from 'zod';
8
8
  import {createCustomModelProviderConfig} from '#core/index.js';
9
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
9
10
  import {toCustomModelProviderConfigDto} from '#presentation/dto/index.js';
10
11
  import {translateModelProviderRouteError} from './errors.js';
11
12
 
12
- export const createCustomModelProviderRoute = defineRoute({
13
- method: 'POST',
14
- path: '/custom-model-providers',
15
- description: 'Create a custom model provider configuration for a workspace',
16
- schema: {
17
- params: z.object({workspaceId: z.string().uuid()}),
18
- body: createCustomModelProviderBodySchema,
19
- response: {
20
- 200: customModelProviderConfigDtoSchema,
13
+ export function createCustomModelProviderRoute(secrets: AgentSecretsClient) {
14
+ return defineRoute({
15
+ method: 'POST',
16
+ path: '/custom-model-providers',
17
+ description: 'Create a custom model provider configuration for a workspace',
18
+ schema: {
19
+ params: z.object({workspaceId: z.string().uuid()}),
20
+ body: createCustomModelProviderBodySchema,
21
+ response: {
22
+ 200: customModelProviderConfigDtoSchema,
23
+ },
21
24
  },
22
- },
23
- errorHandler: translateModelProviderRouteError,
24
- handler: async (request, reply) => {
25
- const {workspaceId} = request.params;
26
- const abortController = new AbortController();
27
- let responseFinished = false;
28
- reply.raw.on('finish', () => {
29
- responseFinished = true;
30
- });
31
- reply.raw.on('close', () => {
32
- if (!responseFinished) abortController.abort();
33
- });
25
+ errorHandler: translateModelProviderRouteError,
26
+ handler: async (request, reply) => {
27
+ const {workspaceId} = request.params;
28
+ const abortController = new AbortController();
29
+ let responseFinished = false;
30
+ reply.raw.on('finish', () => {
31
+ responseFinished = true;
32
+ });
33
+ reply.raw.on('close', () => {
34
+ if (!responseFinished) abortController.abort();
35
+ });
34
36
 
35
- requireWorkspaceAccess({request, workspaceId});
37
+ requireWorkspaceAccess({request, workspaceId});
36
38
 
37
- const config = await createCustomModelProviderConfig({
38
- workspaceId,
39
- body: request.body,
40
- signal: abortController.signal,
41
- });
39
+ const config = await createCustomModelProviderConfig(
40
+ {
41
+ workspaceId,
42
+ body: request.body,
43
+ signal: abortController.signal,
44
+ },
45
+ {secrets},
46
+ );
42
47
 
43
- return toCustomModelProviderConfigDto(config);
44
- },
45
- });
48
+ return toCustomModelProviderConfigDto(config);
49
+ },
50
+ });
51
+ }
@@ -3,29 +3,32 @@ import {requireWorkspaceAccess} from '@shipfox/api-auth-context';
3
3
  import {ClientError, defineRoute} from '@shipfox/node-fastify';
4
4
  import {z} from 'zod';
5
5
  import {deleteModelProviderConfig} from '#core/index.js';
6
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
6
7
 
7
- export const deleteModelProviderConfigRoute = defineRoute({
8
- method: 'DELETE',
9
- path: '/model-providers/:providerId',
10
- description: 'Delete a model provider configuration for a workspace',
11
- schema: {
12
- params: z.object({
13
- workspaceId: z.string().uuid(),
14
- providerId: modelProviderRefSchema,
15
- }),
16
- response: {
17
- 204: z.void(),
8
+ export function createDeleteModelProviderConfigRoute(secrets: AgentSecretsClient) {
9
+ return defineRoute({
10
+ method: 'DELETE',
11
+ path: '/model-providers/:providerId',
12
+ description: 'Delete a model provider configuration for a workspace',
13
+ schema: {
14
+ params: z.object({
15
+ workspaceId: z.string().uuid(),
16
+ providerId: modelProviderRefSchema,
17
+ }),
18
+ response: {
19
+ 204: z.void(),
20
+ },
18
21
  },
19
- },
20
- handler: async (request, reply) => {
21
- const {workspaceId, providerId} = request.params;
22
- requireWorkspaceAccess({request, workspaceId});
22
+ handler: async (request, reply) => {
23
+ const {workspaceId, providerId} = request.params;
24
+ requireWorkspaceAccess({request, workspaceId});
23
25
 
24
- const deleted = await deleteModelProviderConfig({workspaceId, providerId});
25
- if (!deleted) {
26
- throw new ClientError('Model provider configuration not found', 'not-found', {status: 404});
27
- }
26
+ const deleted = await deleteModelProviderConfig({workspaceId, providerId}, {secrets});
27
+ if (!deleted) {
28
+ throw new ClientError('Model provider configuration not found', 'not-found', {status: 404});
29
+ }
28
30
 
29
- reply.code(204);
30
- },
31
- });
31
+ reply.code(204);
32
+ },
33
+ });
34
+ }
@@ -10,33 +10,39 @@ import {
10
10
  discoverCustomModelProviderModels,
11
11
  resolveCustomModelProviderDiscoveryParams,
12
12
  } from '#core/index.js';
13
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
13
14
  import {translateModelProviderRouteError} from './errors.js';
14
15
 
15
- export const discoverCustomModelProviderModelsBySlugRoute = defineRoute({
16
- method: 'POST',
17
- path: '/custom-model-providers/:slug/discover-models',
18
- description: 'Discover models for a stored custom model provider configuration',
19
- schema: {
20
- params: z.object({
21
- workspaceId: z.string().uuid(),
22
- slug: modelProviderRefSchema,
23
- }),
24
- body: discoverCustomModelProviderModelsBySlugBodySchema,
25
- response: {
26
- 200: discoverCustomModelProviderModelsResponseSchema,
16
+ export function createDiscoverCustomModelProviderModelsBySlugRoute(secrets: AgentSecretsClient) {
17
+ return defineRoute({
18
+ method: 'POST',
19
+ path: '/custom-model-providers/:slug/discover-models',
20
+ description: 'Discover models for a stored custom model provider configuration',
21
+ schema: {
22
+ params: z.object({
23
+ workspaceId: z.string().uuid(),
24
+ slug: modelProviderRefSchema,
25
+ }),
26
+ body: discoverCustomModelProviderModelsBySlugBodySchema,
27
+ response: {
28
+ 200: discoverCustomModelProviderModelsResponseSchema,
29
+ },
27
30
  },
28
- },
29
- errorHandler: translateModelProviderRouteError,
30
- handler: async (request) => {
31
- const {workspaceId, slug} = request.params;
32
- requireWorkspaceAccess({request, workspaceId});
31
+ errorHandler: translateModelProviderRouteError,
32
+ handler: async (request) => {
33
+ const {workspaceId, slug} = request.params;
34
+ requireWorkspaceAccess({request, workspaceId});
33
35
 
34
- const discoveryParams = await resolveCustomModelProviderDiscoveryParams({
35
- workspaceId,
36
- providerId: slug,
37
- body: request.body ?? {},
38
- });
36
+ const discoveryParams = await resolveCustomModelProviderDiscoveryParams(
37
+ {
38
+ workspaceId,
39
+ providerId: slug,
40
+ body: request.body ?? {},
41
+ },
42
+ {secrets},
43
+ );
39
44
 
40
- return await discoverCustomModelProviderModels(discoveryParams);
41
- },
42
- });
45
+ return await discoverCustomModelProviderModels(discoveryParams);
46
+ },
47
+ });
48
+ }
@@ -1,5 +1,6 @@
1
1
  import {getModelProviderCredentialKeys} from '@shipfox/api-agent-dto';
2
- import {SecretValueTooLargeError, WorkspaceSecretCapExceededError} from '@shipfox/api-secrets';
2
+ import {secretsInterModuleContract} from '@shipfox/api-secrets-dto/inter-module';
3
+ import {isInterModuleKnownError} from '@shipfox/inter-module';
3
4
  import {EgressDeniedError} from '@shipfox/node-egress-guard';
4
5
  import {ClientError} from '@shipfox/node-fastify';
5
6
  import {
@@ -104,17 +105,16 @@ export function translateModelProviderRouteError(error: unknown): never {
104
105
  });
105
106
  }
106
107
 
107
- if (error instanceof WorkspaceSecretCapExceededError) {
108
- throw new ClientError('Workspace secret cap exceeded', 'workspace-secret-cap-exceeded', {
109
- status: 409,
110
- details: {cap: error.cap},
111
- });
112
- }
113
-
114
- if (error instanceof SecretValueTooLargeError) {
115
- throw new ClientError(error.message, 'value-too-large', {
108
+ if (isInterModuleKnownError(secretsInterModuleContract.methods.setSecrets, error)) {
109
+ if (error.code === 'workspace-secret-cap-exceeded') {
110
+ throw new ClientError('Workspace secret cap exceeded', 'workspace-secret-cap-exceeded', {
111
+ status: 409,
112
+ details: {cap: error.details.cap},
113
+ });
114
+ }
115
+ throw new ClientError('Secret value is too large', 'value-too-large', {
116
116
  status: 400,
117
- details: {max_bytes: error.maxBytes},
117
+ details: {max_bytes: error.details.maxBytes},
118
118
  });
119
119
  }
120
120
 
@@ -1,37 +1,42 @@
1
1
  import {AUTH_USER} from '@shipfox/api-auth-context';
2
2
  import type {RouteGroup} from '@shipfox/node-fastify';
3
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
3
4
  import {createCustomModelProviderRoute} from './create-custom-model-provider.js';
4
- import {deleteModelProviderConfigRoute} from './delete-model-provider-config.js';
5
+ import {createDeleteModelProviderConfigRoute} from './delete-model-provider-config.js';
5
6
  import {discoverCustomModelProviderModelsRoute} from './discover-custom-model-provider-models.js';
6
- import {discoverCustomModelProviderModelsBySlugRoute} from './discover-custom-model-provider-models-by-slug.js';
7
+ import {createDiscoverCustomModelProviderModelsBySlugRoute} from './discover-custom-model-provider-models-by-slug.js';
7
8
  import {listModelProviderCatalogRoute} from './list-model-provider-catalog.js';
8
9
  import {listModelProviderConfigsRoute} from './list-model-provider-configs.js';
9
10
  import {setDefaultHarnessRoute} from './set-default-harness.js';
10
11
  import {setDefaultModelProviderRoute} from './set-default-model-provider.js';
11
- import {updateCustomModelProviderRoute} from './update-custom-model-provider.js';
12
+ import {createUpdateCustomModelProviderRoute} from './update-custom-model-provider.js';
12
13
  import {updateModelProviderDefaultModelRoute} from './update-model-provider-default-model.js';
13
- import {upsertModelProviderConfigRoute} from './upsert-model-provider-config.js';
14
+ import {createUpsertModelProviderConfigRoute} from './upsert-model-provider-config.js';
14
15
 
15
- export const agentRoutes: RouteGroup[] = [
16
- {
17
- prefix: '/workspaces/:workspaceId/agent',
18
- auth: AUTH_USER,
19
- routes: [
20
- listModelProviderConfigsRoute,
21
- createCustomModelProviderRoute,
22
- discoverCustomModelProviderModelsRoute,
23
- discoverCustomModelProviderModelsBySlugRoute,
24
- updateCustomModelProviderRoute,
25
- upsertModelProviderConfigRoute,
26
- updateModelProviderDefaultModelRoute,
27
- deleteModelProviderConfigRoute,
28
- setDefaultHarnessRoute,
29
- setDefaultModelProviderRoute,
30
- ],
31
- },
32
- {
33
- prefix: '/agent',
34
- auth: AUTH_USER,
35
- routes: [listModelProviderCatalogRoute],
36
- },
37
- ];
16
+ export function createAgentRoutes(secrets: AgentSecretsClient): RouteGroup[] {
17
+ return [
18
+ {
19
+ prefix: '/workspaces/:workspaceId/agent',
20
+ auth: AUTH_USER,
21
+ routes: [
22
+ listModelProviderConfigsRoute,
23
+ createCustomModelProviderRoute(secrets),
24
+ discoverCustomModelProviderModelsRoute,
25
+ createDiscoverCustomModelProviderModelsBySlugRoute(secrets),
26
+ createUpdateCustomModelProviderRoute(secrets),
27
+ createUpsertModelProviderConfigRoute(secrets),
28
+ updateModelProviderDefaultModelRoute,
29
+ createDeleteModelProviderConfigRoute(secrets),
30
+ setDefaultHarnessRoute,
31
+ setDefaultModelProviderRoute,
32
+ ],
33
+ },
34
+ {
35
+ prefix: '/agent',
36
+ auth: AUTH_USER,
37
+ routes: [listModelProviderCatalogRoute],
38
+ },
39
+ ];
40
+ }
41
+
42
+ export const agentRoutes = createAgentRoutes(undefined as unknown as AgentSecretsClient);
@@ -6,7 +6,6 @@ import {
6
6
  setUserContext,
7
7
  type UserContextMembership,
8
8
  } from '@shipfox/api-auth-context';
9
- import {getSecretsByNamespace, setSecrets} from '@shipfox/api-secrets';
10
9
  import type {AuthMethod, FastifyRequest} from '@shipfox/node-fastify';
11
10
  import {ClientError, closeApp, createApp} from '@shipfox/node-fastify';
12
11
  import {eq} from 'drizzle-orm';
@@ -20,6 +19,7 @@ import {
20
19
  upsertModelProviderConfig,
21
20
  } from '#db/index.js';
22
21
  import {modelProviderConfigs} from '#db/schema/model-provider-configs.js';
22
+ import {getSecretsByNamespace, setSecrets} from '#test/fixtures/secrets-client.js';
23
23
  import {agentRoutes} from './index.js';
24
24
 
25
25
  vi.mock('@earendil-works/pi-ai', async (importOriginal) => {
@@ -7,44 +7,50 @@ import {requireWorkspaceAccess} from '@shipfox/api-auth-context';
7
7
  import {defineRoute} from '@shipfox/node-fastify';
8
8
  import {z} from 'zod';
9
9
  import {updateCustomModelProviderConfig} from '#core/index.js';
10
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
10
11
  import {toCustomModelProviderConfigDto} from '#presentation/dto/index.js';
11
12
  import {translateModelProviderRouteError} from './errors.js';
12
13
 
13
- export const updateCustomModelProviderRoute = defineRoute({
14
- method: 'PUT',
15
- path: '/custom-model-providers/:slug',
16
- description: 'Update a custom model provider configuration for a workspace',
17
- schema: {
18
- params: z.object({
19
- workspaceId: z.string().uuid(),
20
- slug: modelProviderRefSchema,
21
- }),
22
- body: updateCustomModelProviderBodySchema,
23
- response: {
24
- 200: customModelProviderConfigDtoSchema,
14
+ export function createUpdateCustomModelProviderRoute(secrets: AgentSecretsClient) {
15
+ return defineRoute({
16
+ method: 'PUT',
17
+ path: '/custom-model-providers/:slug',
18
+ description: 'Update a custom model provider configuration for a workspace',
19
+ schema: {
20
+ params: z.object({
21
+ workspaceId: z.string().uuid(),
22
+ slug: modelProviderRefSchema,
23
+ }),
24
+ body: updateCustomModelProviderBodySchema,
25
+ response: {
26
+ 200: customModelProviderConfigDtoSchema,
27
+ },
25
28
  },
26
- },
27
- errorHandler: translateModelProviderRouteError,
28
- handler: async (request, reply) => {
29
- const {workspaceId, slug} = request.params;
30
- const abortController = new AbortController();
31
- let responseFinished = false;
32
- reply.raw.on('finish', () => {
33
- responseFinished = true;
34
- });
35
- reply.raw.on('close', () => {
36
- if (!responseFinished) abortController.abort();
37
- });
29
+ errorHandler: translateModelProviderRouteError,
30
+ handler: async (request, reply) => {
31
+ const {workspaceId, slug} = request.params;
32
+ const abortController = new AbortController();
33
+ let responseFinished = false;
34
+ reply.raw.on('finish', () => {
35
+ responseFinished = true;
36
+ });
37
+ reply.raw.on('close', () => {
38
+ if (!responseFinished) abortController.abort();
39
+ });
38
40
 
39
- requireWorkspaceAccess({request, workspaceId});
41
+ requireWorkspaceAccess({request, workspaceId});
40
42
 
41
- const config = await updateCustomModelProviderConfig({
42
- workspaceId,
43
- providerId: slug,
44
- body: request.body,
45
- signal: abortController.signal,
46
- });
43
+ const config = await updateCustomModelProviderConfig(
44
+ {
45
+ workspaceId,
46
+ providerId: slug,
47
+ body: request.body,
48
+ signal: abortController.signal,
49
+ },
50
+ {secrets},
51
+ );
47
52
 
48
- return toCustomModelProviderConfigDto(config);
49
- },
50
- });
53
+ return toCustomModelProviderConfigDto(config);
54
+ },
55
+ });
56
+ }
@@ -7,47 +7,53 @@ import {requireWorkspaceAccess} from '@shipfox/api-auth-context';
7
7
  import {defineRoute} from '@shipfox/node-fastify';
8
8
  import {z} from 'zod';
9
9
  import {testAndSaveModelProviderConfig} from '#core/index.js';
10
+ import type {AgentSecretsClient} from '#core/secrets-client.js';
10
11
  import {toModelProviderConfigDto} from '#presentation/dto/index.js';
11
12
  import {translateModelProviderRouteError} from './errors.js';
12
13
 
13
- export const upsertModelProviderConfigRoute = defineRoute({
14
- method: 'PUT',
15
- path: '/model-providers/:providerId',
16
- description: 'Test and save a model provider configuration for a workspace',
17
- schema: {
18
- params: z.object({
19
- workspaceId: z.string().uuid(),
20
- providerId: supportedModelProviderIdSchema,
21
- }),
22
- body: updateModelProviderConfigBodySchema,
23
- response: {
24
- 200: modelProviderConfigDtoSchema,
14
+ export function createUpsertModelProviderConfigRoute(secrets: AgentSecretsClient) {
15
+ return defineRoute({
16
+ method: 'PUT',
17
+ path: '/model-providers/:providerId',
18
+ description: 'Test and save a model provider configuration for a workspace',
19
+ schema: {
20
+ params: z.object({
21
+ workspaceId: z.string().uuid(),
22
+ providerId: supportedModelProviderIdSchema,
23
+ }),
24
+ body: updateModelProviderConfigBodySchema,
25
+ response: {
26
+ 200: modelProviderConfigDtoSchema,
27
+ },
25
28
  },
26
- },
27
- errorHandler: translateModelProviderRouteError,
28
- handler: async (request, reply) => {
29
- const {workspaceId, providerId} = request.params;
30
- const abortController = new AbortController();
31
- let responseFinished = false;
32
- reply.raw.on('finish', () => {
33
- responseFinished = true;
34
- });
35
- reply.raw.on('close', () => {
36
- if (!responseFinished) abortController.abort();
37
- });
29
+ errorHandler: translateModelProviderRouteError,
30
+ handler: async (request, reply) => {
31
+ const {workspaceId, providerId} = request.params;
32
+ const abortController = new AbortController();
33
+ let responseFinished = false;
34
+ reply.raw.on('finish', () => {
35
+ responseFinished = true;
36
+ });
37
+ reply.raw.on('close', () => {
38
+ if (!responseFinished) abortController.abort();
39
+ });
38
40
 
39
- const membership = requireWorkspaceAccess({request, workspaceId});
41
+ const membership = requireWorkspaceAccess({request, workspaceId});
40
42
 
41
- const config = await testAndSaveModelProviderConfig({
42
- workspaceId,
43
- providerId,
44
- ...('default_model' in request.body ? {defaultModel: request.body.default_model} : {}),
45
- credentials: request.body.credentials,
46
- editedBy: membership.userId,
47
- setAsDefault: request.body.set_as_default,
48
- signal: abortController.signal,
49
- });
43
+ const config = await testAndSaveModelProviderConfig(
44
+ {
45
+ workspaceId,
46
+ providerId,
47
+ ...('default_model' in request.body ? {defaultModel: request.body.default_model} : {}),
48
+ credentials: request.body.credentials,
49
+ editedBy: membership.userId,
50
+ setAsDefault: request.body.set_as_default,
51
+ signal: abortController.signal,
52
+ },
53
+ {secrets},
54
+ );
50
55
 
51
- return toModelProviderConfigDto(config);
52
- },
53
- });
56
+ return toModelProviderConfigDto(config);
57
+ },
58
+ });
59
+ }