@shipfox/client-agent 0.2.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 (182) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/.storybook/preview.css +4 -0
  3. package/.storybook/preview.tsx +56 -0
  4. package/.turbo/turbo-build.log +2 -0
  5. package/.turbo/turbo-type$colon$emit.log +1 -0
  6. package/.turbo/turbo-type.log +1 -0
  7. package/CHANGELOG.md +93 -0
  8. package/LICENSE +21 -0
  9. package/dist/components/add-custom-provider-card.d.ts +4 -0
  10. package/dist/components/add-custom-provider-card.d.ts.map +1 -0
  11. package/dist/components/add-custom-provider-card.js +41 -0
  12. package/dist/components/add-custom-provider-card.js.map +1 -0
  13. package/dist/components/agent-workflow-example.d.ts +11 -0
  14. package/dist/components/agent-workflow-example.d.ts.map +1 -0
  15. package/dist/components/agent-workflow-example.js +38 -0
  16. package/dist/components/agent-workflow-example.js.map +1 -0
  17. package/dist/components/available-provider-card.d.ts +6 -0
  18. package/dist/components/available-provider-card.d.ts.map +1 -0
  19. package/dist/components/available-provider-card.js +41 -0
  20. package/dist/components/available-provider-card.js.map +1 -0
  21. package/dist/components/available-providers-grid.d.ts +10 -0
  22. package/dist/components/available-providers-grid.d.ts.map +1 -0
  23. package/dist/components/available-providers-grid.js +89 -0
  24. package/dist/components/available-providers-grid.js.map +1 -0
  25. package/dist/components/change-default-model-form.d.ts +9 -0
  26. package/dist/components/change-default-model-form.d.ts.map +1 -0
  27. package/dist/components/change-default-model-form.js +98 -0
  28. package/dist/components/change-default-model-form.js.map +1 -0
  29. package/dist/components/custom-model-provider-api-options.d.ts +7 -0
  30. package/dist/components/custom-model-provider-api-options.d.ts.map +1 -0
  31. package/dist/components/custom-model-provider-api-options.js +23 -0
  32. package/dist/components/custom-model-provider-api-options.js.map +1 -0
  33. package/dist/components/custom-model-provider-form.d.ts +8 -0
  34. package/dist/components/custom-model-provider-form.d.ts.map +1 -0
  35. package/dist/components/custom-model-provider-form.js +667 -0
  36. package/dist/components/custom-model-provider-form.js.map +1 -0
  37. package/dist/components/custom-model-provider-form.stories.d.ts +20 -0
  38. package/dist/components/custom-model-provider-form.stories.d.ts.map +1 -0
  39. package/dist/components/custom-model-provider-form.stories.js +104 -0
  40. package/dist/components/custom-model-provider-form.stories.js.map +1 -0
  41. package/dist/components/custom-model-provider-payload.d.ts +39 -0
  42. package/dist/components/custom-model-provider-payload.d.ts.map +1 -0
  43. package/dist/components/custom-model-provider-payload.js +203 -0
  44. package/dist/components/custom-model-provider-payload.js.map +1 -0
  45. package/dist/components/custom-model-provider-slug.d.ts +3 -0
  46. package/dist/components/custom-model-provider-slug.d.ts.map +1 -0
  47. package/dist/components/custom-model-provider-slug.js +14 -0
  48. package/dist/components/custom-model-provider-slug.js.map +1 -0
  49. package/dist/components/default-model-field.d.ts +16 -0
  50. package/dist/components/default-model-field.d.ts.map +1 -0
  51. package/dist/components/default-model-field.js +60 -0
  52. package/dist/components/default-model-field.js.map +1 -0
  53. package/dist/components/form-errors.d.ts +11 -0
  54. package/dist/components/form-errors.d.ts.map +1 -0
  55. package/dist/components/form-errors.js +101 -0
  56. package/dist/components/form-errors.js.map +1 -0
  57. package/dist/components/harness-availability.d.ts +8 -0
  58. package/dist/components/harness-availability.d.ts.map +1 -0
  59. package/dist/components/harness-availability.js +16 -0
  60. package/dist/components/harness-availability.js.map +1 -0
  61. package/dist/components/harnesses-section.d.ts +4 -0
  62. package/dist/components/harnesses-section.d.ts.map +1 -0
  63. package/dist/components/harnesses-section.js +249 -0
  64. package/dist/components/harnesses-section.js.map +1 -0
  65. package/dist/components/harnesses-section.stories.d.ts +25 -0
  66. package/dist/components/harnesses-section.stories.d.ts.map +1 -0
  67. package/dist/components/harnesses-section.stories.js +138 -0
  68. package/dist/components/harnesses-section.stories.js.map +1 -0
  69. package/dist/components/model-provider-usage-modal.d.ts +11 -0
  70. package/dist/components/model-provider-usage-modal.d.ts.map +1 -0
  71. package/dist/components/model-provider-usage-modal.js +349 -0
  72. package/dist/components/model-provider-usage-modal.js.map +1 -0
  73. package/dist/components/model-provider-usage-modal.stories.d.ts +21 -0
  74. package/dist/components/model-provider-usage-modal.stories.d.ts.map +1 -0
  75. package/dist/components/model-provider-usage-modal.stories.js +115 -0
  76. package/dist/components/model-provider-usage-modal.stories.js.map +1 -0
  77. package/dist/components/model-provider-usage-target.d.ts +14 -0
  78. package/dist/components/model-provider-usage-target.d.ts.map +1 -0
  79. package/dist/components/model-provider-usage-target.js +20 -0
  80. package/dist/components/model-provider-usage-target.js.map +1 -0
  81. package/dist/components/model-providers-section.d.ts +4 -0
  82. package/dist/components/model-providers-section.d.ts.map +1 -0
  83. package/dist/components/model-providers-section.js +668 -0
  84. package/dist/components/model-providers-section.js.map +1 -0
  85. package/dist/components/model-providers-section.stories.d.ts +32 -0
  86. package/dist/components/model-providers-section.stories.d.ts.map +1 -0
  87. package/dist/components/model-providers-section.stories.js +442 -0
  88. package/dist/components/model-providers-section.stories.js.map +1 -0
  89. package/dist/components/provider-search.d.ts +4 -0
  90. package/dist/components/provider-search.d.ts.map +1 -0
  91. package/dist/components/provider-search.js +13 -0
  92. package/dist/components/provider-search.js.map +1 -0
  93. package/dist/components/supported-model-provider-catalog-entry.d.ts +8 -0
  94. package/dist/components/supported-model-provider-catalog-entry.d.ts.map +1 -0
  95. package/dist/components/supported-model-provider-catalog-entry.js +9 -0
  96. package/dist/components/supported-model-provider-catalog-entry.js.map +1 -0
  97. package/dist/components/test-and-save-form.d.ts +10 -0
  98. package/dist/components/test-and-save-form.d.ts.map +1 -0
  99. package/dist/components/test-and-save-form.js +138 -0
  100. package/dist/components/test-and-save-form.js.map +1 -0
  101. package/dist/feature.d.ts +9 -0
  102. package/dist/feature.d.ts.map +1 -0
  103. package/dist/feature.js +13 -0
  104. package/dist/feature.js.map +1 -0
  105. package/dist/hooks/api/model-providers.d.ts +334 -0
  106. package/dist/hooks/api/model-providers.d.ts.map +1 -0
  107. package/dist/hooks/api/model-providers.js +190 -0
  108. package/dist/hooks/api/model-providers.js.map +1 -0
  109. package/dist/index.d.ts +9 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +10 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/pages/model-provider-onboarding-page.d.ts +6 -0
  114. package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -0
  115. package/dist/pages/model-provider-onboarding-page.js +295 -0
  116. package/dist/pages/model-provider-onboarding-page.js.map +1 -0
  117. package/dist/pages/model-provider-onboarding-page.stories.d.ts +27 -0
  118. package/dist/pages/model-provider-onboarding-page.stories.d.ts.map +1 -0
  119. package/dist/pages/model-provider-onboarding-page.stories.js +368 -0
  120. package/dist/pages/model-provider-onboarding-page.stories.js.map +1 -0
  121. package/dist/routes/model-provider.d.ts +5 -0
  122. package/dist/routes/model-provider.d.ts.map +1 -0
  123. package/dist/routes/model-provider.js +28 -0
  124. package/dist/routes/model-provider.js.map +1 -0
  125. package/dist/state/model-provider-onboarding.d.ts +3 -0
  126. package/dist/state/model-provider-onboarding.d.ts.map +1 -0
  127. package/dist/state/model-provider-onboarding.js +28 -0
  128. package/dist/state/model-provider-onboarding.js.map +1 -0
  129. package/dist/tsconfig.test.tsbuildinfo +1 -0
  130. package/package.json +114 -0
  131. package/src/components/add-custom-provider-card.tsx +32 -0
  132. package/src/components/agent-workflow-example.test.ts +74 -0
  133. package/src/components/agent-workflow-example.ts +55 -0
  134. package/src/components/available-provider-card.tsx +39 -0
  135. package/src/components/available-providers-grid.test.tsx +55 -0
  136. package/src/components/available-providers-grid.tsx +106 -0
  137. package/src/components/change-default-model-form.tsx +104 -0
  138. package/src/components/custom-model-provider-api-options.test.ts +18 -0
  139. package/src/components/custom-model-provider-api-options.ts +12 -0
  140. package/src/components/custom-model-provider-form.stories.tsx +91 -0
  141. package/src/components/custom-model-provider-form.tsx +710 -0
  142. package/src/components/custom-model-provider-payload.test.ts +170 -0
  143. package/src/components/custom-model-provider-payload.ts +264 -0
  144. package/src/components/custom-model-provider-slug.test.ts +40 -0
  145. package/src/components/custom-model-provider-slug.ts +23 -0
  146. package/src/components/default-model-field.tsx +85 -0
  147. package/src/components/form-errors.test.ts +159 -0
  148. package/src/components/form-errors.ts +102 -0
  149. package/src/components/harness-availability.test.ts +45 -0
  150. package/src/components/harness-availability.ts +35 -0
  151. package/src/components/harnesses-section.stories.tsx +129 -0
  152. package/src/components/harnesses-section.test.tsx +188 -0
  153. package/src/components/harnesses-section.tsx +245 -0
  154. package/src/components/model-provider-usage-modal.stories.tsx +106 -0
  155. package/src/components/model-provider-usage-modal.test.tsx +210 -0
  156. package/src/components/model-provider-usage-modal.tsx +326 -0
  157. package/src/components/model-provider-usage-target.ts +36 -0
  158. package/src/components/model-providers-section.stories.tsx +345 -0
  159. package/src/components/model-providers-section.test.tsx +831 -0
  160. package/src/components/model-providers-section.tsx +645 -0
  161. package/src/components/provider-search.test.ts +45 -0
  162. package/src/components/provider-search.ts +18 -0
  163. package/src/components/supported-model-provider-catalog-entry.ts +19 -0
  164. package/src/components/test-and-save-form.tsx +166 -0
  165. package/src/feature.ts +12 -0
  166. package/src/hooks/api/model-providers.test.ts +167 -0
  167. package/src/hooks/api/model-providers.ts +281 -0
  168. package/src/index.ts +8 -0
  169. package/src/pages/model-provider-onboarding-page.stories.tsx +284 -0
  170. package/src/pages/model-provider-onboarding-page.test.tsx +368 -0
  171. package/src/pages/model-provider-onboarding-page.tsx +290 -0
  172. package/src/routes/model-provider.tsx +20 -0
  173. package/src/state/model-provider-onboarding.test.ts +55 -0
  174. package/src/state/model-provider-onboarding.ts +34 -0
  175. package/test/fixtures/model-providers.ts +111 -0
  176. package/test/setup.ts +3 -0
  177. package/tsconfig.build.json +9 -0
  178. package/tsconfig.build.tsbuildinfo +1 -0
  179. package/tsconfig.json +3 -0
  180. package/tsconfig.test.json +8 -0
  181. package/vercel.json +8 -0
  182. package/vitest.config.ts +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/custom-model-provider-form.tsx"],"sourcesContent":["import type {CustomModelProviderConfigDto, ModelProviderApi} from '@shipfox/api-agent-dto';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button, IconButton} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {Collapsible, CollapsibleContent, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {ModalBody, ModalFooter} from '@shipfox/react-ui/modal';\nimport {Switch} from '@shipfox/react-ui/switch';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport type {ReactNode, RefObject} from 'react';\nimport {useEffect, useRef, useState} from 'react';\nimport {\n useCreateCustomModelProviderMutation,\n useDiscoverCustomModelProviderModelsBySlugMutation,\n useDiscoverCustomModelProviderModelsMutation,\n useUpdateCustomModelProviderMutation,\n} from '#hooks/api/model-providers.js';\nimport {MODEL_PROVIDER_API_OPTIONS} from './custom-model-provider-api-options.js';\nimport {\n buildCreateCustomModelProviderBody,\n buildDiscoverModelsBody,\n buildDiscoverModelsBySlugBody,\n buildUpdateCustomModelProviderBody,\n type CustomModelProviderFormValues,\n type CustomModelProviderHeaderFormValue,\n type CustomModelProviderModelFormValue,\n createCustomModelProviderFormValues,\n createFormRowId,\n editCustomModelProviderFormValues,\n} from './custom-model-provider-payload.js';\nimport {\n customModelProviderSlugError,\n deriveCustomModelProviderSlug,\n} from './custom-model-provider-slug.js';\nimport {FormFieldSelect} from './default-model-field.js';\nimport {modelProviderConfigErrorField} from './form-errors.js';\n\nexport const CUSTOM_MODEL_PROVIDER_FORM_ID = 'custom-model-provider-form';\n\nexport function CustomModelProviderForm({\n workspaceId,\n existingConfig,\n onSaved,\n}: {\n workspaceId: string;\n existingConfig?: CustomModelProviderConfigDto | undefined;\n onSaved: () => void;\n}) {\n const createMutation = useCreateCustomModelProviderMutation();\n const updateMutation = useUpdateCustomModelProviderMutation();\n const discoverMutation = useDiscoverCustomModelProviderModelsMutation();\n const discoverBySlugMutation = useDiscoverCustomModelProviderModelsBySlugMutation();\n const [slugEdited, setSlugEdited] = useState(existingConfig !== undefined);\n const [formError, setFormError] = useState<string | undefined>();\n const [discoveryStatus, setDiscoveryStatus] = useState<string | undefined>();\n const [removedDefaultNotice, setRemovedDefaultNotice] = useState(false);\n const mountedRef = useRef(true);\n const form = useForm({\n defaultValues: existingConfig\n ? editCustomModelProviderFormValues(existingConfig)\n : createCustomModelProviderFormValues(),\n onSubmit: async ({value}) => {\n setFormError(undefined);\n\n try {\n if (existingConfig) {\n await updateMutation.mutateAsync({\n workspaceId,\n providerId: existingConfig.provider_id,\n body: buildUpdateCustomModelProviderBody(existingConfig, value),\n });\n } else {\n await createMutation.mutateAsync({\n workspaceId,\n body: buildCreateCustomModelProviderBody(value),\n });\n }\n onSaved();\n } catch (error) {\n applyServerError(error);\n }\n },\n });\n\n const isPending =\n createMutation.isPending ||\n updateMutation.isPending ||\n discoverMutation.isPending ||\n discoverBySlugMutation.isPending;\n\n useEffect(() => {\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n async function handleDiscoverModels(values: CustomModelProviderFormValues) {\n setDiscoveryStatus(undefined);\n try {\n const response = existingConfig\n ? await discoverBySlugMutation.mutateAsync({\n workspaceId,\n providerId: existingConfig.provider_id,\n body: buildDiscoverModelsBySlugBody(existingConfig, values),\n })\n : await discoverMutation.mutateAsync({\n workspaceId,\n body: buildDiscoverModelsBody(values),\n });\n if (!mountedRef.current) return;\n const existingIds = new Set(values.models.map((model) => model.id.trim()).filter(Boolean));\n const discovered = response.models.filter((model) => !existingIds.has(model.id));\n form.setFieldValue('models', [\n ...values.models,\n ...discovered.map((model) => ({\n client_id: createFormRowId(),\n id: model.id,\n label: model.label,\n context_window: '',\n max_output_tokens: '',\n input_image: false,\n reasoning: false,\n })),\n ]);\n setDiscoveryStatus(\n discovered.length > 0\n ? `Discovered ${discovered.length} models.`\n : 'No models found - that is normal for some servers. Add models manually.',\n );\n } catch (error) {\n if (!mountedRef.current) return;\n applyServerError(error);\n }\n }\n\n function applyServerError(error: unknown) {\n const mapped = modelProviderConfigErrorField(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n return;\n }\n setFormError(mapped.message);\n }\n\n return (\n <>\n <ModalBody className=\"min-h-0 flex-1 gap-0 overflow-y-auto overflow-x-clip scrollbar\">\n <form\n id={CUSTOM_MODEL_PROVIDER_FORM_ID}\n className=\"flex w-full flex-col gap-24\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Subscribe selector={(state) => state.values}>\n {(values) => (\n <>\n <FormGroup title=\"Provider\">\n <div className=\"grid grid-cols-2 gap-12 max-[640px]:grid-cols-1\">\n <form.Field\n name=\"display_name\"\n validators={{\n onBlur: ({value}) =>\n value.trim() ? undefined : 'Display name is required.',\n onSubmit: ({value}) =>\n value.trim() ? undefined : 'Display name is required.',\n }}\n >\n {(field) => (\n <FormField\n label=\"Display name\"\n id=\"custom-provider-display-name\"\n error={fieldError(field)}\n >\n <FormFieldInput\n value={field.state.value}\n onChange={(event) => {\n const displayName = event.target.value;\n field.handleChange(displayName);\n if (!slugEdited && !existingConfig) {\n form.setFieldValue(\n 'slug',\n deriveCustomModelProviderSlug(displayName),\n );\n }\n }}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"slug\"\n validators={{\n onBlur: ({value}) =>\n existingConfig ? undefined : customModelProviderSlugError(value),\n onSubmit: ({value}) =>\n existingConfig ? undefined : customModelProviderSlugError(value),\n }}\n >\n {(field) => (\n <FormField\n label=\"Provider ID\"\n id=\"custom-provider-slug\"\n error={fieldError(field)}\n description=\"Use this ID in workflow files.\"\n >\n <FormFieldInput\n className=\"font-code\"\n value={field.state.value}\n disabled={existingConfig !== undefined}\n onChange={(event) => {\n setSlugEdited(event.target.value.trim() !== '');\n field.handleChange(event.target.value);\n }}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n </div>\n\n <div className=\"grid grid-cols-2 gap-12 max-[640px]:grid-cols-1\">\n <form.Field name=\"api\">\n {(field) => (\n <FormField label=\"Protocol\" id=\"custom-provider-api\">\n <FormFieldSelect\n value={field.state.value}\n onChange={(event) =>\n field.handleChange(event.target.value as ModelProviderApi)\n }\n onBlur={field.handleBlur}\n >\n {MODEL_PROVIDER_API_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </FormFieldSelect>\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"base_url\"\n validators={{\n onBlur: ({value}) =>\n canParseUrl(value)\n ? undefined\n : 'Enter a full URL, like https://api.openai.com/v1',\n onSubmit: ({value}) =>\n canParseUrl(value)\n ? undefined\n : 'Enter a full URL, like https://api.openai.com/v1',\n }}\n >\n {(field) => (\n <FormField\n label=\"Base URL\"\n id=\"custom-provider-base-url\"\n error={fieldError(field)}\n >\n <FormFieldInput\n className=\"font-code\"\n placeholder=\"https://api.openai.com/v1\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n </div>\n\n <form.Field name=\"api_key\">\n {(field) => (\n <FormField\n label=\"API key\"\n id=\"custom-provider-api-key\"\n description={\n existingConfig\n ? 'Leave blank to keep the current key.'\n : 'Leave blank when the endpoint does not require a key.'\n }\n >\n <FormFieldInput\n type=\"password\"\n autoComplete=\"off\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n </FormGroup>\n\n <FormGroup title=\"Headers\">\n <form.Field name=\"headers\">\n {(field) => (\n <HeaderRows\n rows={field.state.value}\n onChange={(headers) => field.handleChange(headers)}\n />\n )}\n </form.Field>\n </FormGroup>\n\n <FormGroup title=\"Models\">\n <div className=\"flex flex-wrap items-center justify-between gap-8\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n iconLeft=\"downloadLine\"\n disabled={!canParseUrl(values.base_url)}\n isLoading={discoverMutation.isPending || discoverBySlugMutation.isPending}\n onClick={() => {\n void handleDiscoverModels(values);\n }}\n >\n Fetch models\n </Button>\n {discoveryStatus ? (\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\" role=\"status\">\n {discoveryStatus}\n </Text>\n ) : null}\n </div>\n\n <form.Field\n name=\"models\"\n validators={{\n onSubmit: ({value}) =>\n value.some((model) => model.id.trim() && model.label.trim())\n ? undefined\n : 'Add at least one model.',\n }}\n >\n {(field) => (\n <>\n <ModelRows\n rows={field.state.value}\n defaultModel={values.default_model}\n onDefaultRemoved={() => setRemovedDefaultNotice(true)}\n onChange={(models) => field.handleChange(models)}\n onDefaultChange={(defaultModel) => {\n setRemovedDefaultNotice(false);\n form.setFieldValue('default_model', defaultModel);\n }}\n />\n {fieldError(field) ? (\n <Text size=\"sm\" className=\"text-foreground-highlight-error\">\n {fieldError(field)}\n </Text>\n ) : null}\n </>\n )}\n </form.Field>\n\n {removedDefaultNotice ? (\n <Callout role=\"alert\" type=\"warning\">\n <Text size=\"sm\">\n Default model removed - choose a new default or keep none.\n </Text>\n </Callout>\n ) : null}\n <form.Field\n name=\"default_model\"\n validators={{\n onBlur: ({value}) => validateDefaultModel(value, values.models),\n onSubmit: ({value}) => validateDefaultModel(value, values.models),\n }}\n >\n {(field) => (\n <FormField\n label=\"Default model\"\n id=\"custom-provider-default-model\"\n error={fieldError(field)}\n >\n <FormFieldSelect\n value={field.state.value}\n onChange={(event) => {\n setRemovedDefaultNotice(false);\n field.handleChange(event.target.value);\n }}\n onBlur={field.handleBlur}\n >\n <option value=\"\">No default</option>\n {values.models\n .filter((model) => model.id.trim())\n .map((model) => (\n <option key={model.client_id} value={model.id}>\n {model.label.trim() || model.id}\n </option>\n ))}\n </FormFieldSelect>\n </FormField>\n )}\n </form.Field>\n </FormGroup>\n </>\n )}\n </form.Subscribe>\n </form>\n {formError ? (\n <Callout role=\"alert\" type=\"error\" className=\"mt-16\">\n <div className=\"flex flex-col gap-8\">\n <Text size=\"sm\" bold>\n Could not save provider\n </Text>\n <Text size=\"sm\">{formError}</Text>\n </div>\n </Callout>\n ) : null}\n </ModalBody>\n <ModalFooter>\n <Text size=\"sm\" className=\"mr-auto text-foreground-neutral-muted\">\n Saving runs a live test against the endpoint.\n </Text>\n <Button type=\"submit\" form={CUSTOM_MODEL_PROVIDER_FORM_ID} isLoading={isPending}>\n Test & save\n </Button>\n </ModalFooter>\n </>\n );\n}\n\nfunction FormGroup({title, children}: {title: string; children: ReactNode}) {\n return (\n <section className=\"flex flex-col gap-16 border-t border-border-neutral-base pt-16 first:border-t-0 first:pt-0\">\n <Text size=\"xs\" className=\"font-medium uppercase text-foreground-neutral-muted\">\n {title}\n </Text>\n {children}\n </section>\n );\n}\n\nfunction HeaderRows({\n rows,\n onChange,\n}: {\n rows: CustomModelProviderHeaderFormValue[];\n onChange: (rows: CustomModelProviderHeaderFormValue[]) => void;\n}) {\n const addButtonRef = useRef<HTMLButtonElement>(null);\n\n return (\n <div className=\"flex flex-col gap-10\">\n {rows.map((row, index) => (\n <div\n key={row.client_id}\n className=\"grid grid-cols-[1fr_1fr_auto_auto] items-end gap-x-8 gap-y-4 max-[760px]:grid-cols-1\"\n >\n <FormField label=\"Name\" id={`custom-header-name-${row.client_id}`}>\n <FormFieldInput\n className=\"font-code\"\n value={row.name}\n onChange={(event) =>\n updateRow(rows, index, {...row, name: event.target.value}, onChange)\n }\n />\n </FormField>\n <FormField label=\"Value\" id={`custom-header-value-${row.client_id}`}>\n <FormFieldInput\n type={row.secret ? 'password' : 'text'}\n placeholder={row.hasStoredValue ? 'Unchanged' : undefined}\n value={row.value}\n onChange={(event) =>\n updateRow(rows, index, {...row, value: event.target.value}, onChange)\n }\n />\n </FormField>\n <div className=\"flex h-32 items-center gap-8 text-sm text-foreground-neutral-muted\">\n <Switch\n size=\"sm\"\n aria-label={`Mark header ${index + 1} as secret`}\n checked={row.secret}\n onCheckedChange={(checked) =>\n updateRow(rows, index, {...row, secret: checked}, onChange)\n }\n />\n Secret\n </div>\n <IconButton\n type=\"button\"\n size=\"sm\"\n variant=\"transparent\"\n icon=\"deleteBinLine\"\n aria-label=\"Remove header\"\n onClick={() => {\n onChange(rows.filter((_, rowIndex) => rowIndex !== index));\n focusAfterRender(addButtonRef);\n }}\n />\n </div>\n ))}\n <Button\n ref={addButtonRef}\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n iconLeft=\"addLine\"\n onClick={() => {\n const clientId = createFormRowId();\n onChange([\n ...rows,\n {\n client_id: clientId,\n name: '',\n value: '',\n secret: false,\n hasStoredValue: false,\n storedName: '',\n },\n ]);\n focusAfterRender(`custom-header-name-${clientId}`);\n }}\n >\n Add header\n </Button>\n </div>\n );\n}\n\nfunction ModelRows({\n rows,\n defaultModel,\n onChange,\n onDefaultChange,\n onDefaultRemoved,\n}: {\n rows: CustomModelProviderModelFormValue[];\n defaultModel: string;\n onChange: (rows: CustomModelProviderModelFormValue[]) => void;\n onDefaultChange: (value: string) => void;\n onDefaultRemoved: () => void;\n}) {\n const addButtonRef = useRef<HTMLButtonElement>(null);\n\n return (\n <div className=\"flex flex-col gap-10\">\n {rows.map((row, index) => (\n <div key={row.client_id} className=\"rounded-8 border border-border-neutral-base p-12\">\n <div className=\"grid grid-cols-[1fr_1fr_auto] items-end gap-8 max-[760px]:grid-cols-1\">\n <FormField label=\"Model id\" id={`custom-model-id-${row.client_id}`}>\n <FormFieldInput\n className=\"font-code\"\n value={row.id}\n onChange={(event) =>\n updateRow(rows, index, {...row, id: event.target.value}, onChange)\n }\n />\n </FormField>\n <FormField label=\"Label\" id={`custom-model-label-${row.client_id}`}>\n <FormFieldInput\n value={row.label}\n onChange={(event) =>\n updateRow(rows, index, {...row, label: event.target.value}, onChange)\n }\n />\n </FormField>\n <div className=\"flex items-center gap-8\">\n {defaultModel && row.id === defaultModel ? (\n <Badge variant=\"neutral\">Default</Badge>\n ) : null}\n <IconButton\n type=\"button\"\n size=\"sm\"\n variant=\"transparent\"\n icon=\"deleteBinLine\"\n aria-label=\"Remove model\"\n onClick={() => {\n const nextRows = rows.filter((_, rowIndex) => rowIndex !== index);\n onChange(nextRows);\n focusAfterRender(addButtonRef);\n if (row.id === defaultModel) {\n onDefaultChange('');\n onDefaultRemoved();\n }\n }}\n />\n </div>\n </div>\n <Collapsible>\n <CollapsibleTrigger asChild>\n <Button type=\"button\" size=\"sm\" variant=\"transparentMuted\" className=\"mt-8\">\n Defaults: 128k context, 16k output, text-only\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <div className=\"mt-8 grid grid-cols-2 gap-8 max-[640px]:grid-cols-1\">\n <FormField label=\"Context window\" id={`custom-model-context-${row.client_id}`}>\n <FormFieldInput\n type=\"number\"\n inputMode=\"numeric\"\n placeholder=\"128000\"\n value={row.context_window}\n onChange={(event) =>\n updateRow(rows, index, {...row, context_window: event.target.value}, onChange)\n }\n />\n </FormField>\n <FormField label=\"Max output tokens\" id={`custom-model-output-${row.client_id}`}>\n <FormFieldInput\n type=\"number\"\n inputMode=\"numeric\"\n placeholder=\"16384\"\n value={row.max_output_tokens}\n onChange={(event) =>\n updateRow(\n rows,\n index,\n {...row, max_output_tokens: event.target.value},\n onChange,\n )\n }\n />\n </FormField>\n <div className=\"flex h-32 items-center gap-8 text-sm text-foreground-neutral-muted\">\n <Switch\n size=\"sm\"\n aria-label={`Enable image input for model ${index + 1}`}\n checked={row.input_image}\n onCheckedChange={(checked) =>\n updateRow(rows, index, {...row, input_image: checked}, onChange)\n }\n />\n Image input\n </div>\n <div className=\"flex h-32 items-center gap-8 text-sm text-foreground-neutral-muted\">\n <Switch\n size=\"sm\"\n aria-label={`Enable reasoning for model ${index + 1}`}\n checked={row.reasoning}\n onCheckedChange={(checked) =>\n updateRow(rows, index, {...row, reasoning: checked}, onChange)\n }\n />\n Reasoning\n </div>\n </div>\n </CollapsibleContent>\n </Collapsible>\n </div>\n ))}\n <Button\n ref={addButtonRef}\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n iconLeft=\"addLine\"\n onClick={() => {\n const clientId = createFormRowId();\n onChange([\n ...rows,\n {\n client_id: clientId,\n id: '',\n label: '',\n context_window: '',\n max_output_tokens: '',\n input_image: false,\n reasoning: false,\n },\n ]);\n focusAfterRender(`custom-model-id-${clientId}`);\n }}\n >\n Add model\n </Button>\n </div>\n );\n}\n\nfunction updateRow<T>(rows: T[], index: number, row: T, onChange: (rows: T[]) => void) {\n onChange(rows.map((candidate, rowIndex) => (rowIndex === index ? row : candidate)));\n}\n\nfunction focusAfterRender(target: string | RefObject<HTMLElement | null>): void {\n requestAnimationFrame(() => {\n const element = typeof target === 'string' ? document.getElementById(target) : target.current;\n element?.focus();\n });\n}\n\nfunction validateDefaultModel(\n value: string,\n models: CustomModelProviderModelFormValue[],\n): string | undefined {\n return value.trim() && !models.some((model) => model.id.trim() === value.trim())\n ? 'Choose a model from the models list.'\n : undefined;\n}\n\nfunction canParseUrl(value: string): boolean {\n try {\n new URL(value);\n return true;\n } catch {\n return false;\n }\n}\n"],"names":["Badge","Button","IconButton","Callout","Collapsible","CollapsibleContent","CollapsibleTrigger","FormField","FormFieldInput","fieldError","ModalBody","ModalFooter","Switch","Text","useForm","useEffect","useRef","useState","useCreateCustomModelProviderMutation","useDiscoverCustomModelProviderModelsBySlugMutation","useDiscoverCustomModelProviderModelsMutation","useUpdateCustomModelProviderMutation","MODEL_PROVIDER_API_OPTIONS","buildCreateCustomModelProviderBody","buildDiscoverModelsBody","buildDiscoverModelsBySlugBody","buildUpdateCustomModelProviderBody","createCustomModelProviderFormValues","createFormRowId","editCustomModelProviderFormValues","customModelProviderSlugError","deriveCustomModelProviderSlug","FormFieldSelect","modelProviderConfigErrorField","CUSTOM_MODEL_PROVIDER_FORM_ID","CustomModelProviderForm","workspaceId","existingConfig","onSaved","createMutation","updateMutation","discoverMutation","discoverBySlugMutation","slugEdited","setSlugEdited","undefined","formError","setFormError","discoveryStatus","setDiscoveryStatus","removedDefaultNotice","setRemovedDefaultNotice","mountedRef","form","defaultValues","onSubmit","value","mutateAsync","providerId","provider_id","body","error","applyServerError","isPending","current","handleDiscoverModels","values","response","existingIds","Set","models","map","model","id","trim","filter","Boolean","discovered","has","setFieldValue","client_id","label","context_window","max_output_tokens","input_image","reasoning","length","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","Subscribe","selector","state","FormGroup","title","div","Field","name","validators","onBlur","onChange","displayName","target","handleChange","handleBlur","description","disabled","option","canParseUrl","placeholder","type","autoComplete","HeaderRows","rows","headers","size","variant","iconLeft","base_url","isLoading","onClick","role","some","ModelRows","defaultModel","default_model","onDefaultRemoved","onDefaultChange","validateDefaultModel","bold","children","section","addButtonRef","row","index","updateRow","secret","hasStoredValue","aria-label","checked","onCheckedChange","icon","_","rowIndex","focusAfterRender","ref","clientId","storedName","nextRows","asChild","inputMode","candidate","requestAnimationFrame","element","document","getElementById","focus","URL"],"mappings":";AACA,SAAQA,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,MAAM,EAAEC,UAAU,QAAO,2BAA2B;AAC5D,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,WAAW,EAAEC,kBAAkB,EAAEC,kBAAkB,QAAO,gCAAgC;AAClG,SAAQC,SAAS,EAAEC,cAAc,EAAEC,UAAU,QAAO,+BAA+B;AACnF,SAAQC,SAAS,EAAEC,WAAW,QAAO,0BAA0B;AAC/D,SAAQC,MAAM,QAAO,2BAA2B;AAChD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,OAAO,QAAO,uBAAuB;AAE7C,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAClD,SACEC,oCAAoC,EACpCC,kDAAkD,EAClDC,4CAA4C,EAC5CC,oCAAoC,QAC/B,gCAAgC;AACvC,SAAQC,0BAA0B,QAAO,yCAAyC;AAClF,SACEC,kCAAkC,EAClCC,uBAAuB,EACvBC,6BAA6B,EAC7BC,kCAAkC,EAIlCC,mCAAmC,EACnCC,eAAe,EACfC,iCAAiC,QAC5B,qCAAqC;AAC5C,SACEC,4BAA4B,EAC5BC,6BAA6B,QACxB,kCAAkC;AACzC,SAAQC,eAAe,QAAO,2BAA2B;AACzD,SAAQC,6BAA6B,QAAO,mBAAmB;AAE/D,OAAO,MAAMC,gCAAgC,6BAA6B;AAE1E,OAAO,SAASC,wBAAwB,EACtCC,WAAW,EACXC,cAAc,EACdC,OAAO,EAKR;IACC,MAAMC,iBAAiBrB;IACvB,MAAMsB,iBAAiBnB;IACvB,MAAMoB,mBAAmBrB;IACzB,MAAMsB,yBAAyBvB;IAC/B,MAAM,CAACwB,YAAYC,cAAc,GAAG3B,SAASoB,mBAAmBQ;IAChE,MAAM,CAACC,WAAWC,aAAa,GAAG9B;IAClC,MAAM,CAAC+B,iBAAiBC,mBAAmB,GAAGhC;IAC9C,MAAM,CAACiC,sBAAsBC,wBAAwB,GAAGlC,SAAS;IACjE,MAAMmC,aAAapC,OAAO;IAC1B,MAAMqC,OAAOvC,QAAQ;QACnBwC,eAAejB,iBACXR,kCAAkCQ,kBAClCV;QACJ4B,UAAU,OAAO,EAACC,KAAK,EAAC;YACtBT,aAAaF;YAEb,IAAI;gBACF,IAAIR,gBAAgB;oBAClB,MAAMG,eAAeiB,WAAW,CAAC;wBAC/BrB;wBACAsB,YAAYrB,eAAesB,WAAW;wBACtCC,MAAMlC,mCAAmCW,gBAAgBmB;oBAC3D;gBACF,OAAO;oBACL,MAAMjB,eAAekB,WAAW,CAAC;wBAC/BrB;wBACAwB,MAAMrC,mCAAmCiC;oBAC3C;gBACF;gBACAlB;YACF,EAAE,OAAOuB,OAAO;gBACdC,iBAAiBD;YACnB;QACF;IACF;IAEA,MAAME,YACJxB,eAAewB,SAAS,IACxBvB,eAAeuB,SAAS,IACxBtB,iBAAiBsB,SAAS,IAC1BrB,uBAAuBqB,SAAS;IAElChD,UAAU;QACR,OAAO;YACLqC,WAAWY,OAAO,GAAG;QACvB;IACF,GAAG,EAAE;IAEL,eAAeC,qBAAqBC,MAAqC;QACvEjB,mBAAmBJ;QACnB,IAAI;YACF,MAAMsB,WAAW9B,iBACb,MAAMK,uBAAuBe,WAAW,CAAC;gBACvCrB;gBACAsB,YAAYrB,eAAesB,WAAW;gBACtCC,MAAMnC,8BAA8BY,gBAAgB6B;YACtD,KACA,MAAMzB,iBAAiBgB,WAAW,CAAC;gBACjCrB;gBACAwB,MAAMpC,wBAAwB0C;YAChC;YACJ,IAAI,CAACd,WAAWY,OAAO,EAAE;YACzB,MAAMI,cAAc,IAAIC,IAAIH,OAAOI,MAAM,CAACC,GAAG,CAAC,CAACC,QAAUA,MAAMC,EAAE,CAACC,IAAI,IAAIC,MAAM,CAACC;YACjF,MAAMC,aAAaV,SAASG,MAAM,CAACK,MAAM,CAAC,CAACH,QAAU,CAACJ,YAAYU,GAAG,CAACN,MAAMC,EAAE;YAC9EpB,KAAK0B,aAAa,CAAC,UAAU;mBACxBb,OAAOI,MAAM;mBACbO,WAAWN,GAAG,CAAC,CAACC,QAAW,CAAA;wBAC5BQ,WAAWpD;wBACX6C,IAAID,MAAMC,EAAE;wBACZQ,OAAOT,MAAMS,KAAK;wBAClBC,gBAAgB;wBAChBC,mBAAmB;wBACnBC,aAAa;wBACbC,WAAW;oBACb,CAAA;aACD;YACDpC,mBACE4B,WAAWS,MAAM,GAAG,IAChB,CAAC,WAAW,EAAET,WAAWS,MAAM,CAAC,QAAQ,CAAC,GACzC;QAER,EAAE,OAAOzB,OAAO;YACd,IAAI,CAACT,WAAWY,OAAO,EAAE;YACzBF,iBAAiBD;QACnB;IACF;IAEA,SAASC,iBAAiBD,KAAc;QACtC,MAAM0B,SAAStD,8BAA8B4B;QAC7C,IAAI0B,OAAOC,IAAI,KAAK,SAAS;YAC3BnC,KAAKoC,YAAY,CAACF,OAAOG,KAAK,EAAE,CAACC,OAAU,CAAA;oBACzC,GAAGA,IAAI;oBACPC,UAAU;wBAAC,GAAGD,KAAKC,QAAQ;wBAAEC,UAAUN,OAAOO,OAAO;oBAAA;gBACvD,CAAA;YACA;QACF;QACA/C,aAAawC,OAAOO,OAAO;IAC7B;IAEA,qBACE;;0BACE,MAACpF;gBAAUqF,WAAU;;kCACnB,KAAC1C;wBACCoB,IAAIvC;wBACJ6D,WAAU;wBACVC,UAAU;wBACVzC,UAAU,CAAC0C;4BACTA,MAAMC,cAAc;4BACpBD,MAAME,eAAe;4BACrB,KAAK9C,KAAK+C,YAAY;wBACxB;kCAEA,cAAA,KAAC/C,KAAKgD,SAAS;4BAACC,UAAU,CAACC,QAAUA,MAAMrC,MAAM;sCAC9C,CAACA,uBACA;;sDACE,MAACsC;4CAAUC,OAAM;;8DACf,MAACC;oDAAIX,WAAU;;sEACb,KAAC1C,KAAKsD,KAAK;4DACTC,MAAK;4DACLC,YAAY;gEACVC,QAAQ,CAAC,EAACtD,KAAK,EAAC,GACdA,MAAMkB,IAAI,KAAK7B,YAAY;gEAC7BU,UAAU,CAAC,EAACC,KAAK,EAAC,GAChBA,MAAMkB,IAAI,KAAK7B,YAAY;4DAC/B;sEAEC,CAAC6C,sBACA,KAACnF;oEACC0E,OAAM;oEACNR,IAAG;oEACHZ,OAAOpD,WAAWiF;8EAElB,cAAA,KAAClF;wEACCgD,OAAOkC,MAAMa,KAAK,CAAC/C,KAAK;wEACxBuD,UAAU,CAACd;4EACT,MAAMe,cAAcf,MAAMgB,MAAM,CAACzD,KAAK;4EACtCkC,MAAMwB,YAAY,CAACF;4EACnB,IAAI,CAACrE,cAAc,CAACN,gBAAgB;gFAClCgB,KAAK0B,aAAa,CAChB,QACAhD,8BAA8BiF;4EAElC;wEACF;wEACAF,QAAQpB,MAAMyB,UAAU;;;;sEAKhC,KAAC9D,KAAKsD,KAAK;4DACTC,MAAK;4DACLC,YAAY;gEACVC,QAAQ,CAAC,EAACtD,KAAK,EAAC,GACdnB,iBAAiBQ,YAAYf,6BAA6B0B;gEAC5DD,UAAU,CAAC,EAACC,KAAK,EAAC,GAChBnB,iBAAiBQ,YAAYf,6BAA6B0B;4DAC9D;sEAEC,CAACkC,sBACA,KAACnF;oEACC0E,OAAM;oEACNR,IAAG;oEACHZ,OAAOpD,WAAWiF;oEAClB0B,aAAY;8EAEZ,cAAA,KAAC5G;wEACCuF,WAAU;wEACVvC,OAAOkC,MAAMa,KAAK,CAAC/C,KAAK;wEACxB6D,UAAUhF,mBAAmBQ;wEAC7BkE,UAAU,CAACd;4EACTrD,cAAcqD,MAAMgB,MAAM,CAACzD,KAAK,CAACkB,IAAI,OAAO;4EAC5CgB,MAAMwB,YAAY,CAACjB,MAAMgB,MAAM,CAACzD,KAAK;wEACvC;wEACAsD,QAAQpB,MAAMyB,UAAU;;;;;;8DAOlC,MAACT;oDAAIX,WAAU;;sEACb,KAAC1C,KAAKsD,KAAK;4DAACC,MAAK;sEACd,CAAClB,sBACA,KAACnF;oEAAU0E,OAAM;oEAAWR,IAAG;8EAC7B,cAAA,KAACzC;wEACCwB,OAAOkC,MAAMa,KAAK,CAAC/C,KAAK;wEACxBuD,UAAU,CAACd,QACTP,MAAMwB,YAAY,CAACjB,MAAMgB,MAAM,CAACzD,KAAK;wEAEvCsD,QAAQpB,MAAMyB,UAAU;kFAEvB7F,2BAA2BiD,GAAG,CAAC,CAAC+C,uBAC/B,KAACA;gFAA0B9D,OAAO8D,OAAO9D,KAAK;0FAC3C8D,OAAOrC,KAAK;+EADFqC,OAAO9D,KAAK;;;;sEAQnC,KAACH,KAAKsD,KAAK;4DACTC,MAAK;4DACLC,YAAY;gEACVC,QAAQ,CAAC,EAACtD,KAAK,EAAC,GACd+D,YAAY/D,SACRX,YACA;gEACNU,UAAU,CAAC,EAACC,KAAK,EAAC,GAChB+D,YAAY/D,SACRX,YACA;4DACR;sEAEC,CAAC6C,sBACA,KAACnF;oEACC0E,OAAM;oEACNR,IAAG;oEACHZ,OAAOpD,WAAWiF;8EAElB,cAAA,KAAClF;wEACCuF,WAAU;wEACVyB,aAAY;wEACZhE,OAAOkC,MAAMa,KAAK,CAAC/C,KAAK;wEACxBuD,UAAU,CAACd,QAAUP,MAAMwB,YAAY,CAACjB,MAAMgB,MAAM,CAACzD,KAAK;wEAC1DsD,QAAQpB,MAAMyB,UAAU;;;;;;8DAOlC,KAAC9D,KAAKsD,KAAK;oDAACC,MAAK;8DACd,CAAClB,sBACA,KAACnF;4DACC0E,OAAM;4DACNR,IAAG;4DACH2C,aACE/E,iBACI,yCACA;sEAGN,cAAA,KAAC7B;gEACCiH,MAAK;gEACLC,cAAa;gEACblE,OAAOkC,MAAMa,KAAK,CAAC/C,KAAK;gEACxBuD,UAAU,CAACd,QAAUP,MAAMwB,YAAY,CAACjB,MAAMgB,MAAM,CAACzD,KAAK;gEAC1DsD,QAAQpB,MAAMyB,UAAU;;;;;;sDAOlC,KAACX;4CAAUC,OAAM;sDACf,cAAA,KAACpD,KAAKsD,KAAK;gDAACC,MAAK;0DACd,CAAClB,sBACA,KAACiC;wDACCC,MAAMlC,MAAMa,KAAK,CAAC/C,KAAK;wDACvBuD,UAAU,CAACc,UAAYnC,MAAMwB,YAAY,CAACW;;;;sDAMlD,MAACrB;4CAAUC,OAAM;;8DACf,MAACC;oDAAIX,WAAU;;sEACb,KAAC9F;4DACCwH,MAAK;4DACLK,MAAK;4DACLC,SAAQ;4DACRC,UAAS;4DACTX,UAAU,CAACE,YAAYrD,OAAO+D,QAAQ;4DACtCC,WAAWzF,iBAAiBsB,SAAS,IAAIrB,uBAAuBqB,SAAS;4DACzEoE,SAAS;gEACP,KAAKlE,qBAAqBC;4DAC5B;sEACD;;wDAGAlB,gCACC,KAACnC;4DAAKiH,MAAK;4DAAK/B,WAAU;4DAAgCqC,MAAK;sEAC5DpF;6DAED;;;8DAGN,KAACK,KAAKsD,KAAK;oDACTC,MAAK;oDACLC,YAAY;wDACVtD,UAAU,CAAC,EAACC,KAAK,EAAC,GAChBA,MAAM6E,IAAI,CAAC,CAAC7D,QAAUA,MAAMC,EAAE,CAACC,IAAI,MAAMF,MAAMS,KAAK,CAACP,IAAI,MACrD7B,YACA;oDACR;8DAEC,CAAC6C,sBACA;;8EACE,KAAC4C;oEACCV,MAAMlC,MAAMa,KAAK,CAAC/C,KAAK;oEACvB+E,cAAcrE,OAAOsE,aAAa;oEAClCC,kBAAkB,IAAMtF,wBAAwB;oEAChD4D,UAAU,CAACzC,SAAWoB,MAAMwB,YAAY,CAAC5C;oEACzCoE,iBAAiB,CAACH;wEAChBpF,wBAAwB;wEACxBE,KAAK0B,aAAa,CAAC,iBAAiBwD;oEACtC;;gEAED9H,WAAWiF,uBACV,KAAC7E;oEAAKiH,MAAK;oEAAK/B,WAAU;8EACvBtF,WAAWiF;qEAEZ;;;;gDAKTxC,qCACC,KAAC/C;oDAAQiI,MAAK;oDAAQX,MAAK;8DACzB,cAAA,KAAC5G;wDAAKiH,MAAK;kEAAK;;qDAIhB;8DACJ,KAACzE,KAAKsD,KAAK;oDACTC,MAAK;oDACLC,YAAY;wDACVC,QAAQ,CAAC,EAACtD,KAAK,EAAC,GAAKmF,qBAAqBnF,OAAOU,OAAOI,MAAM;wDAC9Df,UAAU,CAAC,EAACC,KAAK,EAAC,GAAKmF,qBAAqBnF,OAAOU,OAAOI,MAAM;oDAClE;8DAEC,CAACoB,sBACA,KAACnF;4DACC0E,OAAM;4DACNR,IAAG;4DACHZ,OAAOpD,WAAWiF;sEAElB,cAAA,MAAC1D;gEACCwB,OAAOkC,MAAMa,KAAK,CAAC/C,KAAK;gEACxBuD,UAAU,CAACd;oEACT9C,wBAAwB;oEACxBuC,MAAMwB,YAAY,CAACjB,MAAMgB,MAAM,CAACzD,KAAK;gEACvC;gEACAsD,QAAQpB,MAAMyB,UAAU;;kFAExB,KAACG;wEAAO9D,OAAM;kFAAG;;oEAChBU,OAAOI,MAAM,CACXK,MAAM,CAAC,CAACH,QAAUA,MAAMC,EAAE,CAACC,IAAI,IAC/BH,GAAG,CAAC,CAACC,sBACJ,KAAC8C;4EAA6B9D,OAAOgB,MAAMC,EAAE;sFAC1CD,MAAMS,KAAK,CAACP,IAAI,MAAMF,MAAMC,EAAE;2EADpBD,MAAMQ,SAAS;;;;;;;;;;;oBAajDlC,0BACC,KAAC3C;wBAAQiI,MAAK;wBAAQX,MAAK;wBAAQ1B,WAAU;kCAC3C,cAAA,MAACW;4BAAIX,WAAU;;8CACb,KAAClF;oCAAKiH,MAAK;oCAAKc,IAAI;8CAAC;;8CAGrB,KAAC/H;oCAAKiH,MAAK;8CAAMhF;;;;yBAGnB;;;0BAEN,MAACnC;;kCACC,KAACE;wBAAKiH,MAAK;wBAAK/B,WAAU;kCAAwC;;kCAGlE,KAAC9F;wBAAOwH,MAAK;wBAASpE,MAAMnB;wBAA+BgG,WAAWnE;kCAAW;;;;;;AAMzF;AAEA,SAASyC,UAAU,EAACC,KAAK,EAAEoC,QAAQ,EAAuC;IACxE,qBACE,MAACC;QAAQ/C,WAAU;;0BACjB,KAAClF;gBAAKiH,MAAK;gBAAK/B,WAAU;0BACvBU;;YAEFoC;;;AAGP;AAEA,SAASlB,WAAW,EAClBC,IAAI,EACJb,QAAQ,EAIT;IACC,MAAMgC,eAAe/H,OAA0B;IAE/C,qBACE,MAAC0F;QAAIX,WAAU;;YACZ6B,KAAKrD,GAAG,CAAC,CAACyE,KAAKC,sBACd,MAACvC;oBAECX,WAAU;;sCAEV,KAACxF;4BAAU0E,OAAM;4BAAOR,IAAI,CAAC,mBAAmB,EAAEuE,IAAIhE,SAAS,EAAE;sCAC/D,cAAA,KAACxE;gCACCuF,WAAU;gCACVvC,OAAOwF,IAAIpC,IAAI;gCACfG,UAAU,CAACd,QACTiD,UAAUtB,MAAMqB,OAAO;wCAAC,GAAGD,GAAG;wCAAEpC,MAAMX,MAAMgB,MAAM,CAACzD,KAAK;oCAAA,GAAGuD;;;sCAIjE,KAACxG;4BAAU0E,OAAM;4BAAQR,IAAI,CAAC,oBAAoB,EAAEuE,IAAIhE,SAAS,EAAE;sCACjE,cAAA,KAACxE;gCACCiH,MAAMuB,IAAIG,MAAM,GAAG,aAAa;gCAChC3B,aAAawB,IAAII,cAAc,GAAG,cAAcvG;gCAChDW,OAAOwF,IAAIxF,KAAK;gCAChBuD,UAAU,CAACd,QACTiD,UAAUtB,MAAMqB,OAAO;wCAAC,GAAGD,GAAG;wCAAExF,OAAOyC,MAAMgB,MAAM,CAACzD,KAAK;oCAAA,GAAGuD;;;sCAIlE,MAACL;4BAAIX,WAAU;;8CACb,KAACnF;oCACCkH,MAAK;oCACLuB,cAAY,CAAC,YAAY,EAAEJ,QAAQ,EAAE,UAAU,CAAC;oCAChDK,SAASN,IAAIG,MAAM;oCACnBI,iBAAiB,CAACD,UAChBJ,UAAUtB,MAAMqB,OAAO;4CAAC,GAAGD,GAAG;4CAAEG,QAAQG;wCAAO,GAAGvC;;gCAEpD;;;sCAGJ,KAAC7G;4BACCuH,MAAK;4BACLK,MAAK;4BACLC,SAAQ;4BACRyB,MAAK;4BACLH,cAAW;4BACXlB,SAAS;gCACPpB,SAASa,KAAKjD,MAAM,CAAC,CAAC8E,GAAGC,WAAaA,aAAaT;gCACnDU,iBAAiBZ;4BACnB;;;mBA1CGC,IAAIhE,SAAS;0BA8CtB,KAAC/E;gBACC2J,KAAKb;gBACLtB,MAAK;gBACLK,MAAK;gBACLC,SAAQ;gBACRC,UAAS;gBACTG,SAAS;oBACP,MAAM0B,WAAWjI;oBACjBmF,SAAS;2BACJa;wBACH;4BACE5C,WAAW6E;4BACXjD,MAAM;4BACNpD,OAAO;4BACP2F,QAAQ;4BACRC,gBAAgB;4BAChBU,YAAY;wBACd;qBACD;oBACDH,iBAAiB,CAAC,mBAAmB,EAAEE,UAAU;gBACnD;0BACD;;;;AAKP;AAEA,SAASvB,UAAU,EACjBV,IAAI,EACJW,YAAY,EACZxB,QAAQ,EACR2B,eAAe,EACfD,gBAAgB,EAOjB;IACC,MAAMM,eAAe/H,OAA0B;IAE/C,qBACE,MAAC0F;QAAIX,WAAU;;YACZ6B,KAAKrD,GAAG,CAAC,CAACyE,KAAKC,sBACd,MAACvC;oBAAwBX,WAAU;;sCACjC,MAACW;4BAAIX,WAAU;;8CACb,KAACxF;oCAAU0E,OAAM;oCAAWR,IAAI,CAAC,gBAAgB,EAAEuE,IAAIhE,SAAS,EAAE;8CAChE,cAAA,KAACxE;wCACCuF,WAAU;wCACVvC,OAAOwF,IAAIvE,EAAE;wCACbsC,UAAU,CAACd,QACTiD,UAAUtB,MAAMqB,OAAO;gDAAC,GAAGD,GAAG;gDAAEvE,IAAIwB,MAAMgB,MAAM,CAACzD,KAAK;4CAAA,GAAGuD;;;8CAI/D,KAACxG;oCAAU0E,OAAM;oCAAQR,IAAI,CAAC,mBAAmB,EAAEuE,IAAIhE,SAAS,EAAE;8CAChE,cAAA,KAACxE;wCACCgD,OAAOwF,IAAI/D,KAAK;wCAChB8B,UAAU,CAACd,QACTiD,UAAUtB,MAAMqB,OAAO;gDAAC,GAAGD,GAAG;gDAAE/D,OAAOgB,MAAMgB,MAAM,CAACzD,KAAK;4CAAA,GAAGuD;;;8CAIlE,MAACL;oCAAIX,WAAU;;wCACZwC,gBAAgBS,IAAIvE,EAAE,KAAK8D,6BAC1B,KAACvI;4CAAM+H,SAAQ;sDAAU;6CACvB;sDACJ,KAAC7H;4CACCuH,MAAK;4CACLK,MAAK;4CACLC,SAAQ;4CACRyB,MAAK;4CACLH,cAAW;4CACXlB,SAAS;gDACP,MAAM4B,WAAWnC,KAAKjD,MAAM,CAAC,CAAC8E,GAAGC,WAAaA,aAAaT;gDAC3DlC,SAASgD;gDACTJ,iBAAiBZ;gDACjB,IAAIC,IAAIvE,EAAE,KAAK8D,cAAc;oDAC3BG,gBAAgB;oDAChBD;gDACF;4CACF;;;;;;sCAIN,MAACrI;;8CACC,KAACE;oCAAmB0J,OAAO;8CACzB,cAAA,KAAC/J;wCAAOwH,MAAK;wCAASK,MAAK;wCAAKC,SAAQ;wCAAmBhC,WAAU;kDAAO;;;8CAI9E,KAAC1F;8CACC,cAAA,MAACqG;wCAAIX,WAAU;;0DACb,KAACxF;gDAAU0E,OAAM;gDAAiBR,IAAI,CAAC,qBAAqB,EAAEuE,IAAIhE,SAAS,EAAE;0DAC3E,cAAA,KAACxE;oDACCiH,MAAK;oDACLwC,WAAU;oDACVzC,aAAY;oDACZhE,OAAOwF,IAAI9D,cAAc;oDACzB6B,UAAU,CAACd,QACTiD,UAAUtB,MAAMqB,OAAO;4DAAC,GAAGD,GAAG;4DAAE9D,gBAAgBe,MAAMgB,MAAM,CAACzD,KAAK;wDAAA,GAAGuD;;;0DAI3E,KAACxG;gDAAU0E,OAAM;gDAAoBR,IAAI,CAAC,oBAAoB,EAAEuE,IAAIhE,SAAS,EAAE;0DAC7E,cAAA,KAACxE;oDACCiH,MAAK;oDACLwC,WAAU;oDACVzC,aAAY;oDACZhE,OAAOwF,IAAI7D,iBAAiB;oDAC5B4B,UAAU,CAACd,QACTiD,UACEtB,MACAqB,OACA;4DAAC,GAAGD,GAAG;4DAAE7D,mBAAmBc,MAAMgB,MAAM,CAACzD,KAAK;wDAAA,GAC9CuD;;;0DAKR,MAACL;gDAAIX,WAAU;;kEACb,KAACnF;wDACCkH,MAAK;wDACLuB,cAAY,CAAC,6BAA6B,EAAEJ,QAAQ,GAAG;wDACvDK,SAASN,IAAI5D,WAAW;wDACxBmE,iBAAiB,CAACD,UAChBJ,UAAUtB,MAAMqB,OAAO;gEAAC,GAAGD,GAAG;gEAAE5D,aAAakE;4DAAO,GAAGvC;;oDAEzD;;;0DAGJ,MAACL;gDAAIX,WAAU;;kEACb,KAACnF;wDACCkH,MAAK;wDACLuB,cAAY,CAAC,2BAA2B,EAAEJ,QAAQ,GAAG;wDACrDK,SAASN,IAAI3D,SAAS;wDACtBkE,iBAAiB,CAACD,UAChBJ,UAAUtB,MAAMqB,OAAO;gEAAC,GAAGD,GAAG;gEAAE3D,WAAWiE;4DAAO,GAAGvC;;oDAEvD;;;;;;;;;mBA/FFiC,IAAIhE,SAAS;0BAuGzB,KAAC/E;gBACC2J,KAAKb;gBACLtB,MAAK;gBACLK,MAAK;gBACLC,SAAQ;gBACRC,UAAS;gBACTG,SAAS;oBACP,MAAM0B,WAAWjI;oBACjBmF,SAAS;2BACJa;wBACH;4BACE5C,WAAW6E;4BACXpF,IAAI;4BACJQ,OAAO;4BACPC,gBAAgB;4BAChBC,mBAAmB;4BACnBC,aAAa;4BACbC,WAAW;wBACb;qBACD;oBACDsE,iBAAiB,CAAC,gBAAgB,EAAEE,UAAU;gBAChD;0BACD;;;;AAKP;AAEA,SAASX,UAAatB,IAAS,EAAEqB,KAAa,EAAED,GAAM,EAAEjC,QAA6B;IACnFA,SAASa,KAAKrD,GAAG,CAAC,CAAC2F,WAAWR,WAAcA,aAAaT,QAAQD,MAAMkB;AACzE;AAEA,SAASP,iBAAiB1C,MAA8C;IACtEkD,sBAAsB;QACpB,MAAMC,UAAU,OAAOnD,WAAW,WAAWoD,SAASC,cAAc,CAACrD,UAAUA,OAAOjD,OAAO;QAC7FoG,SAASG;IACX;AACF;AAEA,SAAS5B,qBACPnF,KAAa,EACbc,MAA2C;IAE3C,OAAOd,MAAMkB,IAAI,MAAM,CAACJ,OAAO+D,IAAI,CAAC,CAAC7D,QAAUA,MAAMC,EAAE,CAACC,IAAI,OAAOlB,MAAMkB,IAAI,MACzE,yCACA7B;AACN;AAEA,SAAS0E,YAAY/D,KAAa;IAChC,IAAI;QACF,IAAIgH,IAAIhH;QACR,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF"}
@@ -0,0 +1,20 @@
1
+ import type { StoryObj } from '@storybook/react';
2
+ interface CustomModelProviderFormStoryProps {
3
+ mode: 'create' | 'edit';
4
+ }
5
+ declare function CustomModelProviderFormStory({ mode }: CustomModelProviderFormStoryProps): import("react").JSX.Element;
6
+ declare const meta: {
7
+ title: string;
8
+ component: typeof CustomModelProviderFormStory;
9
+ parameters: {
10
+ layout: string;
11
+ };
12
+ args: {
13
+ mode: "create";
14
+ };
15
+ };
16
+ export default meta;
17
+ type Story = StoryObj<typeof meta>;
18
+ export declare const Playground: Story;
19
+ export declare const EditStoredSecrets: Story;
20
+ //# sourceMappingURL=custom-model-provider-form.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-model-provider-form.stories.d.ts","sourceRoot":"","sources":["../../src/components/custom-model-provider-form.stories.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAO,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAKrD,UAAU,iCAAiC;IACzC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;CACzB;AAED,iBAAS,4BAA4B,CAAC,EAAC,IAAI,EAAC,EAAE,iCAAiC,+BAiC9E;AAED,QAAA,MAAM,IAAI;;;;;;;;;CAK2C,CAAC;AAEtD,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,UAAU,EAAE,KAAU,CAAC;AAEpC,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC"}
@@ -0,0 +1,104 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { configureApiClient } from '@shipfox/client-api';
3
+ import { Modal, ModalContent, ModalHeader, ModalTitle } from '@shipfox/react-ui/modal';
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
+ import { useMemo, useState } from 'react';
6
+ import { CustomModelProviderForm } from './custom-model-provider-form.js';
7
+ function CustomModelProviderFormStory({ mode }) {
8
+ const [open, setOpen] = useState(true);
9
+ const queryClient = useMemo(()=>new QueryClient({
10
+ defaultOptions: {
11
+ queries: {
12
+ retry: false
13
+ }
14
+ }
15
+ }), []);
16
+ configureApiClient({
17
+ baseUrl: 'https://api.example.test',
18
+ fetchImpl: ()=>Promise.resolve(new Response(JSON.stringify({
19
+ models: []
20
+ })))
21
+ });
22
+ return /*#__PURE__*/ _jsx(QueryClientProvider, {
23
+ client: queryClient,
24
+ children: /*#__PURE__*/ _jsx("div", {
25
+ className: "min-h-screen bg-background-neutral-background p-24",
26
+ children: /*#__PURE__*/ _jsx(Modal, {
27
+ open: open,
28
+ onOpenChange: setOpen,
29
+ children: /*#__PURE__*/ _jsxs(ModalContent, {
30
+ className: "max-h-[min(760px,calc(100vh-32px))]",
31
+ "aria-describedby": undefined,
32
+ children: [
33
+ /*#__PURE__*/ _jsx(ModalTitle, {
34
+ className: "sr-only",
35
+ children: mode === 'edit' ? 'Edit custom provider' : 'Add custom provider'
36
+ }),
37
+ /*#__PURE__*/ _jsx(ModalHeader, {
38
+ title: mode === 'edit' ? 'Edit custom provider' : 'Add custom provider'
39
+ }),
40
+ /*#__PURE__*/ _jsx(CustomModelProviderForm, {
41
+ workspaceId: "11111111-1111-4111-8111-111111111111",
42
+ existingConfig: mode === 'edit' ? customConfig() : undefined,
43
+ onSaved: ()=>setOpen(false)
44
+ })
45
+ ]
46
+ })
47
+ })
48
+ })
49
+ });
50
+ }
51
+ const meta = {
52
+ title: 'Agent/CustomProviderForm',
53
+ component: CustomModelProviderFormStory,
54
+ parameters: {
55
+ layout: 'fullscreen'
56
+ },
57
+ args: {
58
+ mode: 'create'
59
+ }
60
+ };
61
+ export default meta;
62
+ export const Playground = {};
63
+ export const EditStoredSecrets = {
64
+ args: {
65
+ mode: 'edit'
66
+ }
67
+ };
68
+ function customConfig() {
69
+ return {
70
+ kind: 'custom',
71
+ provider_id: 'local-vllm',
72
+ display_name: 'Local vLLM',
73
+ api: 'openai-completions',
74
+ base_url: 'http://localhost:8000/v1',
75
+ headers: [
76
+ {
77
+ name: 'x-region',
78
+ value: 'us'
79
+ }
80
+ ],
81
+ secret_header_names: [
82
+ 'authorization'
83
+ ],
84
+ models: [
85
+ {
86
+ id: 'llama-3.1',
87
+ label: 'Llama 3.1',
88
+ context_window: 128000,
89
+ max_output_tokens: 16384
90
+ },
91
+ {
92
+ id: 'qwen-coder',
93
+ label: 'Qwen Coder',
94
+ context_window: 32768,
95
+ max_output_tokens: 8192
96
+ }
97
+ ],
98
+ default_model: 'llama-3.1',
99
+ created_at: '2026-05-08T00:00:00.000Z',
100
+ updated_at: '2026-05-08T00:00:00.000Z'
101
+ };
102
+ }
103
+
104
+ //# sourceMappingURL=custom-model-provider-form.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/custom-model-provider-form.stories.tsx"],"sourcesContent":["import type {CustomModelProviderConfigDto} from '@shipfox/api-agent-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {Modal, ModalContent, ModalHeader, ModalTitle} from '@shipfox/react-ui/modal';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {useMemo, useState} from 'react';\nimport {CustomModelProviderForm} from './custom-model-provider-form.js';\n\ninterface CustomModelProviderFormStoryProps {\n mode: 'create' | 'edit';\n}\n\nfunction CustomModelProviderFormStory({mode}: CustomModelProviderFormStoryProps) {\n const [open, setOpen] = useState(true);\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: () => Promise.resolve(new Response(JSON.stringify({models: []}))),\n });\n\n return (\n <QueryClientProvider client={queryClient}>\n <div className=\"min-h-screen bg-background-neutral-background p-24\">\n <Modal open={open} onOpenChange={setOpen}>\n <ModalContent\n className=\"max-h-[min(760px,calc(100vh-32px))]\"\n aria-describedby={undefined}\n >\n <ModalTitle className=\"sr-only\">\n {mode === 'edit' ? 'Edit custom provider' : 'Add custom provider'}\n </ModalTitle>\n <ModalHeader title={mode === 'edit' ? 'Edit custom provider' : 'Add custom provider'} />\n <CustomModelProviderForm\n workspaceId=\"11111111-1111-4111-8111-111111111111\"\n existingConfig={mode === 'edit' ? customConfig() : undefined}\n onSaved={() => setOpen(false)}\n />\n </ModalContent>\n </Modal>\n </div>\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Agent/CustomProviderForm',\n component: CustomModelProviderFormStory,\n parameters: {layout: 'fullscreen'},\n args: {mode: 'create'},\n} satisfies Meta<typeof CustomModelProviderFormStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const EditStoredSecrets: Story = {\n args: {mode: 'edit'},\n};\n\nfunction customConfig(): CustomModelProviderConfigDto {\n return {\n kind: 'custom',\n provider_id: 'local-vllm',\n display_name: 'Local vLLM',\n api: 'openai-completions',\n base_url: 'http://localhost:8000/v1',\n headers: [{name: 'x-region', value: 'us'}],\n secret_header_names: ['authorization'],\n models: [\n {\n id: 'llama-3.1',\n label: 'Llama 3.1',\n context_window: 128000,\n max_output_tokens: 16384,\n },\n {\n id: 'qwen-coder',\n label: 'Qwen Coder',\n context_window: 32768,\n max_output_tokens: 8192,\n },\n ],\n default_model: 'llama-3.1',\n created_at: '2026-05-08T00:00:00.000Z',\n updated_at: '2026-05-08T00:00:00.000Z',\n };\n}\n"],"names":["configureApiClient","Modal","ModalContent","ModalHeader","ModalTitle","QueryClient","QueryClientProvider","useMemo","useState","CustomModelProviderForm","CustomModelProviderFormStory","mode","open","setOpen","queryClient","defaultOptions","queries","retry","baseUrl","fetchImpl","Promise","resolve","Response","JSON","stringify","models","client","div","className","onOpenChange","aria-describedby","undefined","title","workspaceId","existingConfig","customConfig","onSaved","meta","component","parameters","layout","args","Playground","EditStoredSecrets","kind","provider_id","display_name","api","base_url","headers","name","value","secret_header_names","id","label","context_window","max_output_tokens","default_model","created_at","updated_at"],"mappings":";AACA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,KAAK,EAAEC,YAAY,EAAEC,WAAW,EAAEC,UAAU,QAAO,0BAA0B;AAErF,SAAQC,WAAW,EAAEC,mBAAmB,QAAO,wBAAwB;AACvE,SAAQC,OAAO,EAAEC,QAAQ,QAAO,QAAQ;AACxC,SAAQC,uBAAuB,QAAO,kCAAkC;AAMxE,SAASC,6BAA6B,EAACC,IAAI,EAAoC;IAC7E,MAAM,CAACC,MAAMC,QAAQ,GAAGL,SAAS;IACjC,MAAMM,cAAcP,QAClB,IAAM,IAAIF,YAAY;YAACU,gBAAgB;gBAACC,SAAS;oBAACC,OAAO;gBAAK;YAAC;QAAC,IAChE,EAAE;IAEJjB,mBAAmB;QACjBkB,SAAS;QACTC,WAAW,IAAMC,QAAQC,OAAO,CAAC,IAAIC,SAASC,KAAKC,SAAS,CAAC;gBAACC,QAAQ,EAAE;YAAA;IAC1E;IAEA,qBACE,KAACnB;QAAoBoB,QAAQZ;kBAC3B,cAAA,KAACa;YAAIC,WAAU;sBACb,cAAA,KAAC3B;gBAAMW,MAAMA;gBAAMiB,cAAchB;0BAC/B,cAAA,MAACX;oBACC0B,WAAU;oBACVE,oBAAkBC;;sCAElB,KAAC3B;4BAAWwB,WAAU;sCACnBjB,SAAS,SAAS,yBAAyB;;sCAE9C,KAACR;4BAAY6B,OAAOrB,SAAS,SAAS,yBAAyB;;sCAC/D,KAACF;4BACCwB,aAAY;4BACZC,gBAAgBvB,SAAS,SAASwB,iBAAiBJ;4BACnDK,SAAS,IAAMvB,QAAQ;;;;;;;AAOrC;AAEA,MAAMwB,OAAO;IACXL,OAAO;IACPM,WAAW5B;IACX6B,YAAY;QAACC,QAAQ;IAAY;IACjCC,MAAM;QAAC9B,MAAM;IAAQ;AACvB;AAEA,eAAe0B,KAAK;AAGpB,OAAO,MAAMK,aAAoB,CAAC,EAAE;AAEpC,OAAO,MAAMC,oBAA2B;IACtCF,MAAM;QAAC9B,MAAM;IAAM;AACrB,EAAE;AAEF,SAASwB;IACP,OAAO;QACLS,MAAM;QACNC,aAAa;QACbC,cAAc;QACdC,KAAK;QACLC,UAAU;QACVC,SAAS;YAAC;gBAACC,MAAM;gBAAYC,OAAO;YAAI;SAAE;QAC1CC,qBAAqB;YAAC;SAAgB;QACtC3B,QAAQ;YACN;gBACE4B,IAAI;gBACJC,OAAO;gBACPC,gBAAgB;gBAChBC,mBAAmB;YACrB;YACA;gBACEH,IAAI;gBACJC,OAAO;gBACPC,gBAAgB;gBAChBC,mBAAmB;YACrB;SACD;QACDC,eAAe;QACfC,YAAY;QACZC,YAAY;IACd;AACF"}
@@ -0,0 +1,39 @@
1
+ import type { CreateCustomModelProviderBodyDto, CustomModelProviderConfigDto, DiscoverCustomModelProviderModelsBodyDto, DiscoverCustomModelProviderModelsBySlugBodyDto, ModelProviderApi, UpdateCustomModelProviderBodyDto } from '@shipfox/api-agent-dto';
2
+ export interface CustomModelProviderHeaderFormValue {
3
+ client_id: string;
4
+ name: string;
5
+ value: string;
6
+ secret: boolean;
7
+ hasStoredValue: boolean;
8
+ storedName: string;
9
+ }
10
+ export interface CustomModelProviderModelFormValue {
11
+ client_id: string;
12
+ id: string;
13
+ label: string;
14
+ context_window: string;
15
+ max_output_tokens: string;
16
+ input_image: boolean;
17
+ reasoning: boolean;
18
+ }
19
+ export interface CustomModelProviderFormValues {
20
+ slug: string;
21
+ display_name: string;
22
+ api: ModelProviderApi;
23
+ base_url: string;
24
+ api_key: string;
25
+ headers: CustomModelProviderHeaderFormValue[];
26
+ models: CustomModelProviderModelFormValue[];
27
+ default_model: string;
28
+ }
29
+ export declare function createCustomModelProviderFormValues(): CustomModelProviderFormValues;
30
+ export declare function editCustomModelProviderFormValues(config: CustomModelProviderConfigDto): CustomModelProviderFormValues;
31
+ export declare function buildCreateCustomModelProviderBody(values: CustomModelProviderFormValues): CreateCustomModelProviderBodyDto;
32
+ export declare function buildUpdateCustomModelProviderBody(config: CustomModelProviderConfigDto, values: CustomModelProviderFormValues): UpdateCustomModelProviderBodyDto;
33
+ export declare function customHeadersDirty(config: CustomModelProviderConfigDto, headers: CustomModelProviderHeaderFormValue[]): boolean;
34
+ export declare function customModelsDirty(config: CustomModelProviderConfigDto, models: CustomModelProviderModelFormValue[]): boolean;
35
+ export declare function buildDiscoverModelsBody(values: CustomModelProviderFormValues): DiscoverCustomModelProviderModelsBodyDto;
36
+ export declare function buildDiscoverModelsBySlugBody(config: CustomModelProviderConfigDto, values: CustomModelProviderFormValues): DiscoverCustomModelProviderModelsBySlugBodyDto;
37
+ export declare function formatBaseUrlHost(baseUrl: string): string;
38
+ export declare function createFormRowId(): string;
39
+ //# sourceMappingURL=custom-model-provider-payload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-model-provider-payload.d.ts","sourceRoot":"","sources":["../../src/components/custom-model-provider-payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gCAAgC,EAEhC,4BAA4B,EAC5B,wCAAwC,EACxC,8CAA8C,EAC9C,gBAAgB,EAChB,gCAAgC,EACjC,MAAM,wBAAwB,CAAC;AAKhC,MAAM,WAAW,kCAAkC;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC9C,MAAM,EAAE,iCAAiC,EAAE,CAAC;IAC5C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,mCAAmC,IAAI,6BAA6B,CAqBnF;AAED,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,4BAA4B,GACnC,6BAA6B,CA4B/B;AAED,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,6BAA6B,GACpC,gCAAgC,CAWlC;AAED,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,4BAA4B,EACpC,MAAM,EAAE,6BAA6B,GACpC,gCAAgC,CAalC;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,4BAA4B,EACpC,OAAO,EAAE,kCAAkC,EAAE,GAC5C,OAAO,CAgBT;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,4BAA4B,EACpC,MAAM,EAAE,iCAAiC,EAAE,GAC1C,OAAO,CAET;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,6BAA6B,GACpC,wCAAwC,CAc1C;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,4BAA4B,EACpC,MAAM,EAAE,6BAA6B,GACpC,8CAA8C,CAOhD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
@@ -0,0 +1,203 @@
1
+ export function createCustomModelProviderFormValues() {
2
+ return {
3
+ slug: '',
4
+ display_name: '',
5
+ api: 'openai-completions',
6
+ base_url: '',
7
+ api_key: '',
8
+ headers: [],
9
+ models: [
10
+ {
11
+ client_id: createFormRowId(),
12
+ id: '',
13
+ label: '',
14
+ context_window: '',
15
+ max_output_tokens: '',
16
+ input_image: false,
17
+ reasoning: false
18
+ }
19
+ ],
20
+ default_model: ''
21
+ };
22
+ }
23
+ export function editCustomModelProviderFormValues(config) {
24
+ return {
25
+ slug: config.provider_id,
26
+ display_name: config.display_name,
27
+ api: config.api,
28
+ base_url: config.base_url,
29
+ api_key: '',
30
+ headers: [
31
+ ...config.headers.map((header)=>({
32
+ client_id: createFormRowId(),
33
+ name: header.name,
34
+ value: header.value,
35
+ secret: false,
36
+ hasStoredValue: false,
37
+ storedName: header.name
38
+ })),
39
+ ...config.secret_header_names.map((name)=>({
40
+ client_id: createFormRowId(),
41
+ name,
42
+ value: '',
43
+ secret: true,
44
+ hasStoredValue: true,
45
+ storedName: normalizeHeaderName(name)
46
+ }))
47
+ ],
48
+ models: config.models.map(modelFormValue),
49
+ default_model: config.default_model ?? ''
50
+ };
51
+ }
52
+ export function buildCreateCustomModelProviderBody(values) {
53
+ return {
54
+ slug: values.slug.trim(),
55
+ display_name: values.display_name.trim(),
56
+ api: values.api,
57
+ base_url: values.base_url.trim(),
58
+ ...values.api_key.trim() ? {
59
+ api_key: values.api_key.trim()
60
+ } : {},
61
+ ...requestHeaders(values.headers).length > 0 ? {
62
+ headers: requestHeaders(values.headers)
63
+ } : {},
64
+ models: requestModels(values.models),
65
+ ...values.default_model.trim() ? {
66
+ default_model: values.default_model.trim()
67
+ } : {}
68
+ };
69
+ }
70
+ export function buildUpdateCustomModelProviderBody(config, values) {
71
+ const body = {};
72
+ if (values.display_name.trim() !== config.display_name) body.display_name = values.display_name.trim();
73
+ if (values.api !== config.api) body.api = values.api;
74
+ if (values.base_url.trim() !== config.base_url) body.base_url = values.base_url.trim();
75
+ if (values.api_key.trim()) body.api_key = values.api_key.trim();
76
+ if (customHeadersDirty(config, values.headers)) body.headers = updateHeaders(values.headers);
77
+ if (customModelsDirty(config, values.models)) body.models = requestModels(values.models);
78
+ const nextDefault = values.default_model.trim() || null;
79
+ if (nextDefault !== config.default_model) body.default_model = nextDefault;
80
+ return body;
81
+ }
82
+ export function customHeadersDirty(config, headers) {
83
+ return JSON.stringify(updateHeaders(headers)) !== JSON.stringify([
84
+ ...config.headers.map((header)=>({
85
+ name: normalizeHeaderName(header.name),
86
+ value: header.value,
87
+ secret: false
88
+ })),
89
+ ...config.secret_header_names.map((name)=>({
90
+ name: normalizeHeaderName(name),
91
+ secret: true,
92
+ keep: true
93
+ }))
94
+ ]);
95
+ }
96
+ export function customModelsDirty(config, models) {
97
+ return JSON.stringify(requestModels(models)) !== JSON.stringify(config.models);
98
+ }
99
+ export function buildDiscoverModelsBody(values) {
100
+ return {
101
+ api: values.api,
102
+ base_url: values.base_url.trim(),
103
+ ...values.api_key.trim() ? {
104
+ api_key: values.api_key.trim()
105
+ } : {},
106
+ ...requestHeaders(values.headers).length > 0 ? {
107
+ headers: requestHeaders(values.headers).map(({ name, value })=>({
108
+ name,
109
+ value
110
+ }))
111
+ } : {}
112
+ };
113
+ }
114
+ export function buildDiscoverModelsBySlugBody(config, values) {
115
+ const body = {};
116
+ if (values.api !== config.api) body.api = values.api;
117
+ if (values.base_url.trim() !== config.base_url) body.base_url = values.base_url.trim();
118
+ if (values.api_key.trim()) body.api_key = values.api_key.trim();
119
+ body.headers = updateHeaders(values.headers);
120
+ return body;
121
+ }
122
+ export function formatBaseUrlHost(baseUrl) {
123
+ try {
124
+ const url = new URL(baseUrl);
125
+ return url.host;
126
+ } catch {
127
+ return baseUrl;
128
+ }
129
+ }
130
+ export function createFormRowId() {
131
+ return crypto.randomUUID();
132
+ }
133
+ function updateHeaders(headers) {
134
+ const result = [];
135
+ for (const header of headers){
136
+ const name = normalizeHeaderName(header.name);
137
+ if (!name) continue;
138
+ if (header.secret && header.hasStoredValue && header.value.trim() === '' && name === header.storedName) {
139
+ result.push({
140
+ name,
141
+ secret: true,
142
+ keep: true
143
+ });
144
+ continue;
145
+ }
146
+ result.push({
147
+ name,
148
+ value: header.value.trim(),
149
+ secret: header.secret
150
+ });
151
+ }
152
+ return result;
153
+ }
154
+ function requestHeaders(headers) {
155
+ return updateHeaders(headers).flatMap((header)=>'value' in header && header.value ? [
156
+ {
157
+ name: header.name,
158
+ value: header.value,
159
+ secret: header.secret
160
+ }
161
+ ] : []);
162
+ }
163
+ function requestModels(models) {
164
+ return models.flatMap((model)=>{
165
+ const id = model.id.trim();
166
+ const label = model.label.trim();
167
+ if (!id || !label) return [];
168
+ return [
169
+ {
170
+ id,
171
+ label,
172
+ ...model.context_window.trim() ? {
173
+ context_window: Number(model.context_window)
174
+ } : {},
175
+ ...model.max_output_tokens.trim() ? {
176
+ max_output_tokens: Number(model.max_output_tokens)
177
+ } : {},
178
+ ...model.input_image ? {
179
+ input_image: true
180
+ } : {},
181
+ ...model.reasoning ? {
182
+ reasoning: true
183
+ } : {}
184
+ }
185
+ ];
186
+ });
187
+ }
188
+ function modelFormValue(model) {
189
+ return {
190
+ client_id: createFormRowId(),
191
+ id: model.id,
192
+ label: model.label,
193
+ context_window: model.context_window?.toString() ?? '',
194
+ max_output_tokens: model.max_output_tokens?.toString() ?? '',
195
+ input_image: model.input_image ?? false,
196
+ reasoning: model.reasoning ?? false
197
+ };
198
+ }
199
+ function normalizeHeaderName(name) {
200
+ return name.trim().toLowerCase();
201
+ }
202
+
203
+ //# sourceMappingURL=custom-model-provider-payload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/custom-model-provider-payload.ts"],"sourcesContent":["import type {\n CreateCustomModelProviderBodyDto,\n CustomAgentModelDto,\n CustomModelProviderConfigDto,\n DiscoverCustomModelProviderModelsBodyDto,\n DiscoverCustomModelProviderModelsBySlugBodyDto,\n ModelProviderApi,\n UpdateCustomModelProviderBodyDto,\n} from '@shipfox/api-agent-dto';\n\ntype CreateHeaderRequest = NonNullable<CreateCustomModelProviderBodyDto['headers']>[number];\ntype UpdateHeaderRequest = NonNullable<UpdateCustomModelProviderBodyDto['headers']>[number];\n\nexport interface CustomModelProviderHeaderFormValue {\n client_id: string;\n name: string;\n value: string;\n secret: boolean;\n hasStoredValue: boolean;\n storedName: string;\n}\n\nexport interface CustomModelProviderModelFormValue {\n client_id: string;\n id: string;\n label: string;\n context_window: string;\n max_output_tokens: string;\n input_image: boolean;\n reasoning: boolean;\n}\n\nexport interface CustomModelProviderFormValues {\n slug: string;\n display_name: string;\n api: ModelProviderApi;\n base_url: string;\n api_key: string;\n headers: CustomModelProviderHeaderFormValue[];\n models: CustomModelProviderModelFormValue[];\n default_model: string;\n}\n\nexport function createCustomModelProviderFormValues(): CustomModelProviderFormValues {\n return {\n slug: '',\n display_name: '',\n api: 'openai-completions',\n base_url: '',\n api_key: '',\n headers: [],\n models: [\n {\n client_id: createFormRowId(),\n id: '',\n label: '',\n context_window: '',\n max_output_tokens: '',\n input_image: false,\n reasoning: false,\n },\n ],\n default_model: '',\n };\n}\n\nexport function editCustomModelProviderFormValues(\n config: CustomModelProviderConfigDto,\n): CustomModelProviderFormValues {\n return {\n slug: config.provider_id,\n display_name: config.display_name,\n api: config.api,\n base_url: config.base_url,\n api_key: '',\n headers: [\n ...config.headers.map((header) => ({\n client_id: createFormRowId(),\n name: header.name,\n value: header.value,\n secret: false,\n hasStoredValue: false,\n storedName: header.name,\n })),\n ...config.secret_header_names.map((name) => ({\n client_id: createFormRowId(),\n name,\n value: '',\n secret: true,\n hasStoredValue: true,\n storedName: normalizeHeaderName(name),\n })),\n ],\n models: config.models.map(modelFormValue),\n default_model: config.default_model ?? '',\n };\n}\n\nexport function buildCreateCustomModelProviderBody(\n values: CustomModelProviderFormValues,\n): CreateCustomModelProviderBodyDto {\n return {\n slug: values.slug.trim(),\n display_name: values.display_name.trim(),\n api: values.api,\n base_url: values.base_url.trim(),\n ...(values.api_key.trim() ? {api_key: values.api_key.trim()} : {}),\n ...(requestHeaders(values.headers).length > 0 ? {headers: requestHeaders(values.headers)} : {}),\n models: requestModels(values.models),\n ...(values.default_model.trim() ? {default_model: values.default_model.trim()} : {}),\n };\n}\n\nexport function buildUpdateCustomModelProviderBody(\n config: CustomModelProviderConfigDto,\n values: CustomModelProviderFormValues,\n): UpdateCustomModelProviderBodyDto {\n const body: UpdateCustomModelProviderBodyDto = {};\n if (values.display_name.trim() !== config.display_name)\n body.display_name = values.display_name.trim();\n if (values.api !== config.api) body.api = values.api;\n if (values.base_url.trim() !== config.base_url) body.base_url = values.base_url.trim();\n if (values.api_key.trim()) body.api_key = values.api_key.trim();\n if (customHeadersDirty(config, values.headers)) body.headers = updateHeaders(values.headers);\n if (customModelsDirty(config, values.models)) body.models = requestModels(values.models);\n\n const nextDefault = values.default_model.trim() || null;\n if (nextDefault !== config.default_model) body.default_model = nextDefault;\n return body;\n}\n\nexport function customHeadersDirty(\n config: CustomModelProviderConfigDto,\n headers: CustomModelProviderHeaderFormValue[],\n): boolean {\n return (\n JSON.stringify(updateHeaders(headers)) !==\n JSON.stringify([\n ...config.headers.map((header) => ({\n name: normalizeHeaderName(header.name),\n value: header.value,\n secret: false,\n })),\n ...config.secret_header_names.map((name) => ({\n name: normalizeHeaderName(name),\n secret: true,\n keep: true,\n })),\n ])\n );\n}\n\nexport function customModelsDirty(\n config: CustomModelProviderConfigDto,\n models: CustomModelProviderModelFormValue[],\n): boolean {\n return JSON.stringify(requestModels(models)) !== JSON.stringify(config.models);\n}\n\nexport function buildDiscoverModelsBody(\n values: CustomModelProviderFormValues,\n): DiscoverCustomModelProviderModelsBodyDto {\n return {\n api: values.api,\n base_url: values.base_url.trim(),\n ...(values.api_key.trim() ? {api_key: values.api_key.trim()} : {}),\n ...(requestHeaders(values.headers).length > 0\n ? {\n headers: requestHeaders(values.headers).map(({name, value}) => ({\n name,\n value,\n })),\n }\n : {}),\n };\n}\n\nexport function buildDiscoverModelsBySlugBody(\n config: CustomModelProviderConfigDto,\n values: CustomModelProviderFormValues,\n): DiscoverCustomModelProviderModelsBySlugBodyDto {\n const body: DiscoverCustomModelProviderModelsBySlugBodyDto = {};\n if (values.api !== config.api) body.api = values.api;\n if (values.base_url.trim() !== config.base_url) body.base_url = values.base_url.trim();\n if (values.api_key.trim()) body.api_key = values.api_key.trim();\n body.headers = updateHeaders(values.headers);\n return body;\n}\n\nexport function formatBaseUrlHost(baseUrl: string): string {\n try {\n const url = new URL(baseUrl);\n return url.host;\n } catch {\n return baseUrl;\n }\n}\n\nexport function createFormRowId(): string {\n return crypto.randomUUID();\n}\n\nfunction updateHeaders(headers: CustomModelProviderHeaderFormValue[]): UpdateHeaderRequest[] {\n const result: UpdateHeaderRequest[] = [];\n for (const header of headers) {\n const name = normalizeHeaderName(header.name);\n if (!name) continue;\n if (\n header.secret &&\n header.hasStoredValue &&\n header.value.trim() === '' &&\n name === header.storedName\n ) {\n result.push({name, secret: true, keep: true});\n continue;\n }\n result.push({name, value: header.value.trim(), secret: header.secret});\n }\n return result;\n}\n\nfunction requestHeaders(headers: CustomModelProviderHeaderFormValue[]): CreateHeaderRequest[] {\n return updateHeaders(headers).flatMap((header) =>\n 'value' in header && header.value\n ? [{name: header.name, value: header.value, secret: header.secret}]\n : [],\n );\n}\n\nfunction requestModels(models: CustomModelProviderModelFormValue[]): CustomAgentModelDto[] {\n return models.flatMap((model) => {\n const id = model.id.trim();\n const label = model.label.trim();\n if (!id || !label) return [];\n return [\n {\n id,\n label,\n ...(model.context_window.trim() ? {context_window: Number(model.context_window)} : {}),\n ...(model.max_output_tokens.trim()\n ? {max_output_tokens: Number(model.max_output_tokens)}\n : {}),\n ...(model.input_image ? {input_image: true} : {}),\n ...(model.reasoning ? {reasoning: true} : {}),\n },\n ];\n });\n}\n\nfunction modelFormValue(model: CustomAgentModelDto): CustomModelProviderModelFormValue {\n return {\n client_id: createFormRowId(),\n id: model.id,\n label: model.label,\n context_window: model.context_window?.toString() ?? '',\n max_output_tokens: model.max_output_tokens?.toString() ?? '',\n input_image: model.input_image ?? false,\n reasoning: model.reasoning ?? false,\n };\n}\n\nfunction normalizeHeaderName(name: string): string {\n return name.trim().toLowerCase();\n}\n"],"names":["createCustomModelProviderFormValues","slug","display_name","api","base_url","api_key","headers","models","client_id","createFormRowId","id","label","context_window","max_output_tokens","input_image","reasoning","default_model","editCustomModelProviderFormValues","config","provider_id","map","header","name","value","secret","hasStoredValue","storedName","secret_header_names","normalizeHeaderName","modelFormValue","buildCreateCustomModelProviderBody","values","trim","requestHeaders","length","requestModels","buildUpdateCustomModelProviderBody","body","customHeadersDirty","updateHeaders","customModelsDirty","nextDefault","JSON","stringify","keep","buildDiscoverModelsBody","buildDiscoverModelsBySlugBody","formatBaseUrlHost","baseUrl","url","URL","host","crypto","randomUUID","result","push","flatMap","model","Number","toString","toLowerCase"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function deriveCustomModelProviderSlug(displayName: string): string;
2
+ export declare function customModelProviderSlugError(slug: string): string | undefined;
3
+ //# sourceMappingURL=custom-model-provider-slug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-model-provider-slug.d.ts","sourceRoot":"","sources":["../../src/components/custom-model-provider-slug.ts"],"names":[],"mappings":"AAMA,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CASzE;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK7E"}
@@ -0,0 +1,14 @@
1
+ import { isReservedModelProviderId, MODEL_PROVIDER_SLUG_PATTERN } from '@shipfox/api-agent-dto';
2
+ const MAX_SLUG_LENGTH = 40;
3
+ const SLUG_FORMAT_ERROR = 'Use 3-40 lowercase letters, digits, and dashes; start and end with a letter or digit.';
4
+ export function deriveCustomModelProviderSlug(displayName) {
5
+ return displayName.normalize('NFKD').replace(/\p{Diacritic}/gu, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, MAX_SLUG_LENGTH).replace(/-+$/g, '');
6
+ }
7
+ export function customModelProviderSlugError(slug) {
8
+ const trimmed = slug.trim();
9
+ if (!MODEL_PROVIDER_SLUG_PATTERN.test(trimmed)) return SLUG_FORMAT_ERROR;
10
+ if (isReservedModelProviderId(trimmed)) return 'This id is reserved for a built-in provider.';
11
+ return undefined;
12
+ }
13
+
14
+ //# sourceMappingURL=custom-model-provider-slug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/custom-model-provider-slug.ts"],"sourcesContent":["import {isReservedModelProviderId, MODEL_PROVIDER_SLUG_PATTERN} from '@shipfox/api-agent-dto';\n\nconst MAX_SLUG_LENGTH = 40;\nconst SLUG_FORMAT_ERROR =\n 'Use 3-40 lowercase letters, digits, and dashes; start and end with a letter or digit.';\n\nexport function deriveCustomModelProviderSlug(displayName: string): string {\n return displayName\n .normalize('NFKD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, MAX_SLUG_LENGTH)\n .replace(/-+$/g, '');\n}\n\nexport function customModelProviderSlugError(slug: string): string | undefined {\n const trimmed = slug.trim();\n if (!MODEL_PROVIDER_SLUG_PATTERN.test(trimmed)) return SLUG_FORMAT_ERROR;\n if (isReservedModelProviderId(trimmed)) return 'This id is reserved for a built-in provider.';\n return undefined;\n}\n"],"names":["isReservedModelProviderId","MODEL_PROVIDER_SLUG_PATTERN","MAX_SLUG_LENGTH","SLUG_FORMAT_ERROR","deriveCustomModelProviderSlug","displayName","normalize","replace","toLowerCase","slice","customModelProviderSlugError","slug","trimmed","trim","test","undefined"],"mappings":"AAAA,SAAQA,yBAAyB,EAAEC,2BAA2B,QAAO,yBAAyB;AAE9F,MAAMC,kBAAkB;AACxB,MAAMC,oBACJ;AAEF,OAAO,SAASC,8BAA8BC,WAAmB;IAC/D,OAAOA,YACJC,SAAS,CAAC,QACVC,OAAO,CAAC,mBAAmB,IAC3BC,WAAW,GACXD,OAAO,CAAC,eAAe,KACvBA,OAAO,CAAC,YAAY,IACpBE,KAAK,CAAC,GAAGP,iBACTK,OAAO,CAAC,QAAQ;AACrB;AAEA,OAAO,SAASG,6BAA6BC,IAAY;IACvD,MAAMC,UAAUD,KAAKE,IAAI;IACzB,IAAI,CAACZ,4BAA4Ba,IAAI,CAACF,UAAU,OAAOT;IACvD,IAAIH,0BAA0BY,UAAU,OAAO;IAC/C,OAAOG;AACT"}
@@ -0,0 +1,16 @@
1
+ import type { ModelProviderCatalogEntryDto } from '@shipfox/api-agent-dto';
2
+ import type { ComponentProps, ReactNode } from 'react';
3
+ export declare const LATEST_MODEL_VALUE = "__latest__";
4
+ export declare function DefaultModelField({ entry, value, error, children, onChange, onBlur, }: {
5
+ entry: ModelProviderCatalogEntryDto;
6
+ value: string;
7
+ error: string | undefined;
8
+ children?: ReactNode;
9
+ onChange: ComponentProps<'select'>['onChange'];
10
+ onBlur: ComponentProps<'select'>['onBlur'];
11
+ }): import("react").JSX.Element;
12
+ export declare function defaultModelFormValue(defaultModel: string | null | undefined): string;
13
+ export declare function selectedModelForCredentialsPayload(selectedModel: string): string | null;
14
+ export declare function selectedModelForModelPayload(selectedModel: string): string | null;
15
+ export declare function FormFieldSelect({ className, ...props }: ComponentProps<'select'>): import("react").JSX.Element;
16
+ //# sourceMappingURL=default-model-field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-model-field.d.ts","sourceRoot":"","sources":["../../src/components/default-model-field.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,4BAA4B,EAAC,MAAM,wBAAwB,CAAC;AAGzE,OAAO,KAAK,EAAC,cAAc,EAAE,SAAS,EAAC,MAAM,OAAO,CAAC;AAErD,eAAO,MAAM,kBAAkB,eAAe,CAAC;AAE/C,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE;IACD,KAAK,EAAE,4BAA4B,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC5C,+BAmBA;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAErF;AAED,wBAAgB,kCAAkC,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEvF;AAED,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEjF;AAkBD,wBAAgB,eAAe,CAAC,EAAC,SAAS,EAAE,GAAG,KAAK,EAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAc9E"}