catalog.ts 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. /** Shared projection of the live LLM registry into the browser model catalog. */
  2. import type { Context } from '@deepseek-ai/cordis'
  3. import type {
  4. ModelCatalog,
  5. ModelReasoning,
  6. ModelSelection,
  7. } from './types.ts'
  8. /**
  9. * Build the browser model catalog without requiring a Session.
  10. * @param ctx - Host context carrying the live LLM registry.
  11. * @param defaultSelection - deployment default used before a Session selects a model.
  12. * @returns successful non-empty provider groups and isolated provider failures.
  13. */
  14. export async function buildModelCatalog(
  15. ctx: Context,
  16. defaultSelection: ModelSelection = ctx.agentDefaultModel.currentSelection(),
  17. ): Promise<ModelCatalog> {
  18. const providers = ctx.llm.listProviders()
  19. const catalog = await Promise.all(providers.map(async (provider) => {
  20. try {
  21. const models = await ctx.llm.listModels(provider.id)
  22. const entries = await Promise.all(models.map(async (model) => {
  23. const resolved = await ctx.llm.resolveModelInfo(provider.id, model.id)
  24. const reasoning: ModelReasoning | undefined = resolved.reasoning === undefined
  25. ? undefined
  26. : {
  27. efforts: resolved.reasoning.efforts.map(effort => ({
  28. id: effort.id,
  29. name: effort.name,
  30. ...(effort.description === undefined ? {} : { description: effort.description }),
  31. })),
  32. ...(resolved.reasoning.defaultEffort === undefined
  33. ? {}
  34. : { defaultEffort: resolved.reasoning.defaultEffort }),
  35. }
  36. return {
  37. id: model.id,
  38. name: model.name,
  39. ...(model.description === undefined ? {} : { description: model.description }),
  40. ...(reasoning === undefined ? {} : { reasoning }),
  41. }
  42. }))
  43. return {
  44. kind: 'group' as const,
  45. group: { id: provider.id, name: provider.name, models: entries },
  46. }
  47. } catch (error) {
  48. return {
  49. kind: 'failure' as const,
  50. failure: {
  51. id: provider.id,
  52. name: provider.name,
  53. message: error instanceof Error ? error.message : String(error),
  54. },
  55. }
  56. }
  57. }))
  58. return {
  59. default: { ...defaultSelection },
  60. routableProviders: providers.map(provider => provider.id),
  61. groups: catalog.flatMap(item => item.kind === 'group' ? [item.group] : [])
  62. .filter(group => group.models.length > 0),
  63. failures: catalog.flatMap(item => item.kind === 'failure' ? [item.failure] : []),
  64. }
  65. }