@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.
- package/.storybook/main.ts +1 -0
- package/.storybook/preview.css +4 -0
- package/.storybook/preview.tsx +56 -0
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +93 -0
- package/LICENSE +21 -0
- package/dist/components/add-custom-provider-card.d.ts +4 -0
- package/dist/components/add-custom-provider-card.d.ts.map +1 -0
- package/dist/components/add-custom-provider-card.js +41 -0
- package/dist/components/add-custom-provider-card.js.map +1 -0
- package/dist/components/agent-workflow-example.d.ts +11 -0
- package/dist/components/agent-workflow-example.d.ts.map +1 -0
- package/dist/components/agent-workflow-example.js +38 -0
- package/dist/components/agent-workflow-example.js.map +1 -0
- package/dist/components/available-provider-card.d.ts +6 -0
- package/dist/components/available-provider-card.d.ts.map +1 -0
- package/dist/components/available-provider-card.js +41 -0
- package/dist/components/available-provider-card.js.map +1 -0
- package/dist/components/available-providers-grid.d.ts +10 -0
- package/dist/components/available-providers-grid.d.ts.map +1 -0
- package/dist/components/available-providers-grid.js +89 -0
- package/dist/components/available-providers-grid.js.map +1 -0
- package/dist/components/change-default-model-form.d.ts +9 -0
- package/dist/components/change-default-model-form.d.ts.map +1 -0
- package/dist/components/change-default-model-form.js +98 -0
- package/dist/components/change-default-model-form.js.map +1 -0
- package/dist/components/custom-model-provider-api-options.d.ts +7 -0
- package/dist/components/custom-model-provider-api-options.d.ts.map +1 -0
- package/dist/components/custom-model-provider-api-options.js +23 -0
- package/dist/components/custom-model-provider-api-options.js.map +1 -0
- package/dist/components/custom-model-provider-form.d.ts +8 -0
- package/dist/components/custom-model-provider-form.d.ts.map +1 -0
- package/dist/components/custom-model-provider-form.js +667 -0
- package/dist/components/custom-model-provider-form.js.map +1 -0
- package/dist/components/custom-model-provider-form.stories.d.ts +20 -0
- package/dist/components/custom-model-provider-form.stories.d.ts.map +1 -0
- package/dist/components/custom-model-provider-form.stories.js +104 -0
- package/dist/components/custom-model-provider-form.stories.js.map +1 -0
- package/dist/components/custom-model-provider-payload.d.ts +39 -0
- package/dist/components/custom-model-provider-payload.d.ts.map +1 -0
- package/dist/components/custom-model-provider-payload.js +203 -0
- package/dist/components/custom-model-provider-payload.js.map +1 -0
- package/dist/components/custom-model-provider-slug.d.ts +3 -0
- package/dist/components/custom-model-provider-slug.d.ts.map +1 -0
- package/dist/components/custom-model-provider-slug.js +14 -0
- package/dist/components/custom-model-provider-slug.js.map +1 -0
- package/dist/components/default-model-field.d.ts +16 -0
- package/dist/components/default-model-field.d.ts.map +1 -0
- package/dist/components/default-model-field.js +60 -0
- package/dist/components/default-model-field.js.map +1 -0
- package/dist/components/form-errors.d.ts +11 -0
- package/dist/components/form-errors.d.ts.map +1 -0
- package/dist/components/form-errors.js +101 -0
- package/dist/components/form-errors.js.map +1 -0
- package/dist/components/harness-availability.d.ts +8 -0
- package/dist/components/harness-availability.d.ts.map +1 -0
- package/dist/components/harness-availability.js +16 -0
- package/dist/components/harness-availability.js.map +1 -0
- package/dist/components/harnesses-section.d.ts +4 -0
- package/dist/components/harnesses-section.d.ts.map +1 -0
- package/dist/components/harnesses-section.js +249 -0
- package/dist/components/harnesses-section.js.map +1 -0
- package/dist/components/harnesses-section.stories.d.ts +25 -0
- package/dist/components/harnesses-section.stories.d.ts.map +1 -0
- package/dist/components/harnesses-section.stories.js +138 -0
- package/dist/components/harnesses-section.stories.js.map +1 -0
- package/dist/components/model-provider-usage-modal.d.ts +11 -0
- package/dist/components/model-provider-usage-modal.d.ts.map +1 -0
- package/dist/components/model-provider-usage-modal.js +349 -0
- package/dist/components/model-provider-usage-modal.js.map +1 -0
- package/dist/components/model-provider-usage-modal.stories.d.ts +21 -0
- package/dist/components/model-provider-usage-modal.stories.d.ts.map +1 -0
- package/dist/components/model-provider-usage-modal.stories.js +115 -0
- package/dist/components/model-provider-usage-modal.stories.js.map +1 -0
- package/dist/components/model-provider-usage-target.d.ts +14 -0
- package/dist/components/model-provider-usage-target.d.ts.map +1 -0
- package/dist/components/model-provider-usage-target.js +20 -0
- package/dist/components/model-provider-usage-target.js.map +1 -0
- package/dist/components/model-providers-section.d.ts +4 -0
- package/dist/components/model-providers-section.d.ts.map +1 -0
- package/dist/components/model-providers-section.js +668 -0
- package/dist/components/model-providers-section.js.map +1 -0
- package/dist/components/model-providers-section.stories.d.ts +32 -0
- package/dist/components/model-providers-section.stories.d.ts.map +1 -0
- package/dist/components/model-providers-section.stories.js +442 -0
- package/dist/components/model-providers-section.stories.js.map +1 -0
- package/dist/components/provider-search.d.ts +4 -0
- package/dist/components/provider-search.d.ts.map +1 -0
- package/dist/components/provider-search.js +13 -0
- package/dist/components/provider-search.js.map +1 -0
- package/dist/components/supported-model-provider-catalog-entry.d.ts +8 -0
- package/dist/components/supported-model-provider-catalog-entry.d.ts.map +1 -0
- package/dist/components/supported-model-provider-catalog-entry.js +9 -0
- package/dist/components/supported-model-provider-catalog-entry.js.map +1 -0
- package/dist/components/test-and-save-form.d.ts +10 -0
- package/dist/components/test-and-save-form.d.ts.map +1 -0
- package/dist/components/test-and-save-form.js +138 -0
- package/dist/components/test-and-save-form.js.map +1 -0
- package/dist/feature.d.ts +9 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/feature.js +13 -0
- package/dist/feature.js.map +1 -0
- package/dist/hooks/api/model-providers.d.ts +334 -0
- package/dist/hooks/api/model-providers.d.ts.map +1 -0
- package/dist/hooks/api/model-providers.js +190 -0
- package/dist/hooks/api/model-providers.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/pages/model-provider-onboarding-page.d.ts +6 -0
- package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -0
- package/dist/pages/model-provider-onboarding-page.js +295 -0
- package/dist/pages/model-provider-onboarding-page.js.map +1 -0
- package/dist/pages/model-provider-onboarding-page.stories.d.ts +27 -0
- package/dist/pages/model-provider-onboarding-page.stories.d.ts.map +1 -0
- package/dist/pages/model-provider-onboarding-page.stories.js +368 -0
- package/dist/pages/model-provider-onboarding-page.stories.js.map +1 -0
- package/dist/routes/model-provider.d.ts +5 -0
- package/dist/routes/model-provider.d.ts.map +1 -0
- package/dist/routes/model-provider.js +28 -0
- package/dist/routes/model-provider.js.map +1 -0
- package/dist/state/model-provider-onboarding.d.ts +3 -0
- package/dist/state/model-provider-onboarding.d.ts.map +1 -0
- package/dist/state/model-provider-onboarding.js +28 -0
- package/dist/state/model-provider-onboarding.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +114 -0
- package/src/components/add-custom-provider-card.tsx +32 -0
- package/src/components/agent-workflow-example.test.ts +74 -0
- package/src/components/agent-workflow-example.ts +55 -0
- package/src/components/available-provider-card.tsx +39 -0
- package/src/components/available-providers-grid.test.tsx +55 -0
- package/src/components/available-providers-grid.tsx +106 -0
- package/src/components/change-default-model-form.tsx +104 -0
- package/src/components/custom-model-provider-api-options.test.ts +18 -0
- package/src/components/custom-model-provider-api-options.ts +12 -0
- package/src/components/custom-model-provider-form.stories.tsx +91 -0
- package/src/components/custom-model-provider-form.tsx +710 -0
- package/src/components/custom-model-provider-payload.test.ts +170 -0
- package/src/components/custom-model-provider-payload.ts +264 -0
- package/src/components/custom-model-provider-slug.test.ts +40 -0
- package/src/components/custom-model-provider-slug.ts +23 -0
- package/src/components/default-model-field.tsx +85 -0
- package/src/components/form-errors.test.ts +159 -0
- package/src/components/form-errors.ts +102 -0
- package/src/components/harness-availability.test.ts +45 -0
- package/src/components/harness-availability.ts +35 -0
- package/src/components/harnesses-section.stories.tsx +129 -0
- package/src/components/harnesses-section.test.tsx +188 -0
- package/src/components/harnesses-section.tsx +245 -0
- package/src/components/model-provider-usage-modal.stories.tsx +106 -0
- package/src/components/model-provider-usage-modal.test.tsx +210 -0
- package/src/components/model-provider-usage-modal.tsx +326 -0
- package/src/components/model-provider-usage-target.ts +36 -0
- package/src/components/model-providers-section.stories.tsx +345 -0
- package/src/components/model-providers-section.test.tsx +831 -0
- package/src/components/model-providers-section.tsx +645 -0
- package/src/components/provider-search.test.ts +45 -0
- package/src/components/provider-search.ts +18 -0
- package/src/components/supported-model-provider-catalog-entry.ts +19 -0
- package/src/components/test-and-save-form.tsx +166 -0
- package/src/feature.ts +12 -0
- package/src/hooks/api/model-providers.test.ts +167 -0
- package/src/hooks/api/model-providers.ts +281 -0
- package/src/index.ts +8 -0
- package/src/pages/model-provider-onboarding-page.stories.tsx +284 -0
- package/src/pages/model-provider-onboarding-page.test.tsx +368 -0
- package/src/pages/model-provider-onboarding-page.tsx +290 -0
- package/src/routes/model-provider.tsx +20 -0
- package/src/state/model-provider-onboarding.test.ts +55 -0
- package/src/state/model-provider-onboarding.ts +34 -0
- package/test/fixtures/model-providers.ts +111 -0
- package/test/setup.ts +3 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vercel.json +8 -0
- 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":"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"}
|
|
@@ -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 @@
|
|
|
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"}
|