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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"remark-math": "6.0.0",
"sonner": "2.0.5",
"tailwindcss": "4.1.4",
"token.js": "npm:[email protected].27",
"token.js": "npm:[email protected].29",
"tw-animate-css": "1.2.8",
"ulidx": "2.4.1",
"unified": "11.0.5",
Expand Down
14 changes: 12 additions & 2 deletions web-app/src/consts/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const predefinedProviders = [
{
active: true,
api_key: '',
base_url: 'https://api.anthropic.com',
base_url: 'https://api.anthropic.com/v1',
provider: 'anthropic',
explore_models_url:
'https://docs.anthropic.com/en/docs/about-claude/models',
Expand Down Expand Up @@ -127,11 +127,21 @@ export const predefinedProviders = [
},
],
models: [],
custom_header: [
{
header: 'anthropic-version',
value: '2023-06-01'
},
{
header: 'anthropic-dangerous-direct-browser-access',
value: 'true'
}
]
},
{
active: true,
api_key: '',
base_url: 'https://api.cohere.ai/compatibility/v1',
base_url: 'https://api.cohere.ai/v1',
explore_models_url: 'https://docs.cohere.com/v2/docs/models',
provider: 'cohere',
settings: [
Expand Down
75 changes: 74 additions & 1 deletion web-app/src/hooks/useModelProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
selectedModel: null,
deletedModels: [],
getModelBy: (modelId: string) => {
const provider = get().providers.find(
(provider) => provider.provider === get().selectedProvider
)
if (!provider) return undefined
return provider.models.find((model) => model.id === modelId)
},

Check warning on line 38 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

33-38 lines are not covered with tests
setProviders: (providers) =>
set((state) => {
const existingProviders = state.providers
Expand Down Expand Up @@ -66,7 +66,7 @@
// Ensure deletedModels is always an array
const currentDeletedModels = Array.isArray(state.deletedModels)
? state.deletedModels
: []

Check warning on line 69 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

69 line is not covered with tests

const updatedProviders = providers.map((provider) => {
const existingProvider = existingProviders.find(
Expand All @@ -83,14 +83,14 @@
('id' in e || 'model' in e) &&
typeof (e.id ?? e.model) === 'string' &&
!models.some((m) => m.id === e.id) &&
!currentDeletedModels.includes(e.id)

Check warning on line 86 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

86 line is not covered with tests
),
...models,
]
const updatedModels = provider.models?.map((model) => {
const settings =
(legacyModels && legacyModels?.length > 0
? legacyModels

Check warning on line 93 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

93 line is not covered with tests
: models
).find(
(m) =>
Expand All @@ -112,18 +112,18 @@
...provider,
models: provider.persist ? updatedModels : mergedModels,
settings: provider.settings.map((setting) => {
const existingSetting = provider.persist
? undefined
: existingProvider?.settings?.find(
(x) => x.key === setting.key
)
return {
...setting,
controller_props: {
...setting.controller_props,
...(existingSetting?.controller_props || {}),
},
}

Check warning on line 126 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

115-126 lines are not covered with tests
}),
api_key: existingProvider?.api_key || provider.api_key,
base_url: existingProvider?.base_url || provider.base_url,
Expand All @@ -140,18 +140,18 @@
}
}),
updateProvider: (providerName, data) => {
set((state) => ({
providers: state.providers.map((provider) => {
if (provider.provider === providerName) {
return {
...provider,
...data,
}
}
return provider
}),
}))
},

Check warning on line 154 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

143-154 lines are not covered with tests
getProviderByName: (providerName: string) => {
const provider = get().providers.find(
(provider) => provider.provider === providerName
Expand All @@ -161,21 +161,21 @@
},
selectModelProvider: (providerName: string, modelName: string) => {
// Find the model object
const provider = get().providers.find(
(provider) => provider.provider === providerName
)

Check warning on line 166 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

164-166 lines are not covered with tests

let modelObject: Model | undefined = undefined

Check warning on line 168 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

168 line is not covered with tests

if (provider && provider.models) {
modelObject = provider.models.find((model) => model.id === modelName)
}

Check warning on line 172 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

170-172 lines are not covered with tests

// Update state with provider name and model object
set({
selectedProvider: providerName,
selectedModel: modelObject || null,
})

Check warning on line 178 in web-app/src/hooks/useModelProvider.ts

View workflow job for this annotation

GitHub Actions / coverage-check

175-178 lines are not covered with tests

return modelObject
},
Expand Down Expand Up @@ -320,9 +320,82 @@
})
}

if (version <= 3 && state?.providers) {
state.providers.forEach((provider) => {
// Migrate Anthropic provider base URL and add custom headers
if (provider.provider === 'anthropic') {
if (provider.base_url === 'https://api.anthropic.com') {
provider.base_url = 'https://api.anthropic.com/v1'
}

// Update base-url in settings
if (provider.settings) {
const baseUrlSetting = provider.settings.find(
(s) => s.key === 'base-url'
)
if (
baseUrlSetting?.controller_props?.value ===
'https://api.anthropic.com'
) {
baseUrlSetting.controller_props.value =
'https://api.anthropic.com/v1'
}
if (
baseUrlSetting?.controller_props?.placeholder ===
'https://api.anthropic.com'
) {
baseUrlSetting.controller_props.placeholder =
'https://api.anthropic.com/v1'
}
}

if (!provider.custom_header) {
provider.custom_header = [
{
header: 'anthropic-version',
value: '2023-06-01',
},
{
header: 'anthropic-dangerous-direct-browser-access',
value: 'true',
},
]
}
}

if (provider.provider === 'cohere') {
if (provider.base_url === 'https://api.cohere.ai/compatibility/v1') {
provider.base_url = 'https://api.cohere.ai/v1'
}

// Update base-url in settings
if (provider.settings) {
const baseUrlSetting = provider.settings.find(
(s) => s.key === 'base-url'
)
if (
baseUrlSetting?.controller_props?.value ===
'https://api.cohere.ai/compatibility/v1'
) {
baseUrlSetting.controller_props.value =
'https://api.cohere.ai/v1'
}
if (
baseUrlSetting?.controller_props?.placeholder ===
'https://api.cohere.ai/compatibility/v1'
) {
baseUrlSetting.controller_props.placeholder =
'https://api.cohere.ai/v1'
}
}
}

})
}

return state
},
version: 3,
version: 4,
}
)
)
2 changes: 1 addition & 1 deletion web-app/src/routes/settings/providers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function ModelProviders() {
toast.error(t('providerAlreadyExists', { name }))
return
}
const newProvider = {
const newProvider: ProviderObject = {
provider: name,
active: true,
models: [],
Expand Down
6 changes: 6 additions & 0 deletions web-app/src/services/providers/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ export class TauriProvidersService extends DefaultProvidersService {
headers['Authorization'] = `Bearer ${provider.api_key}`
}

if (provider.custom_header) {
provider.custom_header.forEach((header) => {
headers[header.header] = header.value
})
}

// Always use Tauri's fetch to avoid CORS issues
const response = await fetchTauri(`${provider.base_url}/models`, {
method: 'GET',
Expand Down
6 changes: 6 additions & 0 deletions web-app/src/types/modelProviders.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type ProviderObject = {
settings: ProviderSetting[]
models: Model[]
persist?: boolean
custom_header?: ProviderCustomHeader[] | null
}

/**
Expand All @@ -71,3 +72,8 @@ type ProxyOptions = {
verifyHostSSL: boolean
noProxy: string
}

type ProviderCustomHeader = {
header: string
value: string
}
Loading