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
242 changes: 121 additions & 121 deletions resources/acp-registry/registry.json

Large diffs are not rendered by default.

7,226 changes: 4,537 additions & 2,689 deletions resources/model-db/providers.json

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/main/provider/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ const PROVIDER_DEFAULTS: LLM_PROVIDER_BASE[] = [
models: 'https://ollama.com/library'
}
},
{
id: 'astrlink',
name: 'AstrLink',
apiType: 'openai-completions',
apiKey: '',
baseUrl: 'http://127.0.0.1:18317/v1',
enable: false,
websites: {
official: 'https://github.com/Calcium-Ion/AstrLink',
apiKey: 'https://github.com/Calcium-Ion/AstrLink#2-create-an-access-token',
docs: 'https://github.com/Calcium-Ion/AstrLink#3-connect-your-ai-agent',
models: 'https://github.com/Calcium-Ion/AstrLink#3-connect-your-ai-agent'
}
},
{
id: 'magpie',
name: 'Magpie',
Expand Down
8 changes: 8 additions & 0 deletions src/main/provider/providerRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,14 @@ const PROVIDER_ID_REGISTRY = new Map<string, AiSdkProviderDefinition>([
...OPENAI_BASE
})
],
[
'astrlink',
createDefinition({
...OPENAI_BASE,
credentialStrategy: 'api-key',
embeddingStrategy: 'none'
})
],
[
'magpie',
createDefinition({
Expand Down
33 changes: 33 additions & 0 deletions src/renderer/src/assets/llm-icons/astrlink.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/renderer/src/components/icons/modelIconRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import aihubmixColorIcon from '@/assets/llm-icons/aihubmix.png?url'
import defaultIcon from '@/assets/logo.png?url'
import metaColorIcon from '@/assets/llm-icons/meta.svg?url'
import lmstudioColorIcon from '@/assets/llm-icons/lmstudio.svg?url'
import astrlinkIcon from '@/assets/llm-icons/astrlink.svg?url'
import magpieIcon from '@/assets/llm-icons/magpie.png?url'
import _302aiIcon from '@/assets/llm-icons/302ai.svg?url'
import modelscopeColorIcon from '@/assets/llm-icons/modelscope-color.svg?url'
Expand Down Expand Up @@ -222,6 +223,7 @@ export const modelIcons = {
'novita.ai': novitaAiIcon,
astraflow: astraflowIcon,
'astraflow-cn': astraflowIcon,
astrlink: astrlinkIcon,
magpie: magpieIcon,
default: defaultIcon
} as const
Expand Down
63 changes: 63 additions & 0 deletions test/main/provider/basicApiKeyProviders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,69 @@ describe('basic API-key provider registrations', () => {
})
})

it('discovers AstrLink models with a user token and the configured local port', async () => {
const defaults = DEFAULT_PROVIDERS.find(({ id }) => id === 'astrlink')!
expect(defaults).toMatchObject({
name: 'AstrLink',
apiType: 'openai-completions',
apiKey: '',
baseUrl: 'http://127.0.0.1:18317/v1',
enable: false,
websites: { defaultBaseUrl: 'http://127.0.0.1:18317/v1' }
})
expect(DEFAULT_PROVIDERS.indexOf(defaults)).toBeLessThan(
DEFAULT_PROVIDERS.findIndex(({ id }) => id === 'magpie')
)
expect(resolveAiSdkProviderDefinition(defaults)).toMatchObject({
runtimeKind: 'openai-compatible',
modelSource: 'openai',
checkStrategy: 'fetch-models',
credentialStrategy: 'api-key',
embeddingStrategy: 'none'
})
const fetchMock = vi.fn().mockImplementation(async () =>
Response.json({
object: 'list',
data: [
{ id: 'claude-sonnet-4-6', owned_by: 'anthropic' },
{ id: 'my-routing-alias', owned_by: 'system' }
],
has_more: false
})
)
global.fetch = fetchMock
const missingToken = new AiSdkProvider(defaults, createProviderSettings())
fetchMock.mockResolvedValueOnce(new Response('Unauthorized', { status: 401 }))
expect(await missingToken.check()).toMatchObject({ isOk: false })

const provider = new AiSdkProvider(
{ ...defaults, apiKey: 'astrlink-test-token', baseUrl: 'http://127.0.0.1:28419/v1' },
createProviderSettings()
)
await expect(provider.fetchModels({ suppressErrors: false })).resolves.toEqual([
expect.objectContaining({
id: 'claude-sonnet-4-6',
providerId: 'astrlink',
ownedBy: 'anthropic'
}),
expect.objectContaining({
id: 'my-routing-alias',
providerId: 'astrlink',
ownedBy: 'system'
})
])
await expect(provider.check()).resolves.toEqual({ isOk: true, errorMsg: null })
expect(fetchMock).toHaveBeenCalledWith(
'http://127.0.0.1:28419/v1/models',
expect.objectContaining({
headers: expect.objectContaining({ Authorization: 'Bearer astrlink-test-token' })
})
)

fetchMock.mockImplementation(async () => new Response('Unauthorized', { status: 401 }))
expect(await provider.check()).toMatchObject({ isOk: false })
})

it('discovers Magpie models and routing groups with the built-in local credentials', async () => {
const defaults = DEFAULT_PROVIDERS.find(({ id }) => id === 'magpie')!
expect(defaults).toMatchObject({
Expand Down
9 changes: 9 additions & 0 deletions test/renderer/components/ModelIcon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('ModelIcon', () => {
const ModelIcon = (await import('@/components/icons/ModelIcon.vue')).default
const nvidiaIcon = (await import('@/assets/llm-icons/nvidia-color.svg?url')).default
const huggingFaceIcon = (await import('@/assets/llm-icons/huggingface-color.svg?url')).default
const astrlinkIcon = (await import('@/assets/llm-icons/astrlink.svg?url')).default
const alibabaIcon = (await import('@/assets/llm-icons/alibabacloud-color.svg?url')).default
const tokenlabIcon = (await import('@/assets/llm-icons/tokenlab.webp?url')).default
const daoxeIcon = (await import('@/assets/llm-icons/daoxe.png?url')).default
Expand All @@ -105,6 +106,12 @@ describe('ModelIcon', () => {
modelId: 'huggingface'
}
})
const astrlink = mount(ModelIcon, {
props: {
modelId: 'astrlink',
isDark: true
}
})
const alibabaTokenPlan = mount(ModelIcon, {
props: {
modelId: 'alibaba-token-plan'
Expand Down Expand Up @@ -155,6 +162,8 @@ describe('ModelIcon', () => {

expect(nvidia.get('img').attributes('src')).toBe(nvidiaIcon)
expect(huggingface.get('img').attributes('src')).toBe(huggingFaceIcon)
expect(astrlink.get('img').attributes('src')).toBe(astrlinkIcon)
expect(astrlink.get('img').classes()).not.toContain('invert')
expect(alibabaTokenPlan.get('img').attributes('src')).toBe(alibabaIcon)
expect(tokenlab.get('img').attributes('src')).toBe(tokenlabIcon)
expect(daoxe.get('img').attributes('src')).toBe(daoxeIcon)
Expand Down