agent-default-model.spec.ts 3.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /** Default Agent model settings layered over a real settings provider. */
  2. import { describe, expect, it } from 'vitest'
  3. import { Context } from '@deepseek-ai/cordis'
  4. import AgentDefaultModelConfig, { AGENT_DEFAULT_MODEL_SETTINGS_NAMESPACE } from '../src/index.ts'
  5. import { SettingsProvider } from '@deepseek-ai/dsh-settings'
  6. import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
  7. import { ReasoningEffortId } from '@deepseek-ai/dsh-llm'
  8. /** The smallest real provider: one in-memory document, always writable. */
  9. class MemorySettings extends SettingsProvider {
  10. doc: Record<string, unknown> = {}
  11. get writable(): boolean {
  12. return true
  13. }
  14. protected load(): Promise<Record<string, unknown>> {
  15. return Promise.resolve(structuredClone(this.doc))
  16. }
  17. protected persist(ns: SettingsNamespace, section: Record<string, unknown>): Promise<void> {
  18. this.doc = { ...this.doc, [ns]: structuredClone(section) }
  19. return Promise.resolve()
  20. }
  21. }
  22. async function boot(): Promise<{
  23. ctx: Context
  24. settingsFiber: Context['fiber']
  25. defaultModel: AgentDefaultModelConfig
  26. }> {
  27. const ctx = new Context()
  28. const settingsFiber = ctx.plugin(MemorySettings)
  29. await settingsFiber.await()
  30. await ctx.plugin(AgentDefaultModelConfig, {
  31. provider: 'deepseek-official',
  32. model: 'deepseek-v4-flash',
  33. })
  34. return { ctx, settingsFiber, defaultModel: ctx.agentDefaultModel }
  35. }
  36. describe('AgentDefaultModelConfig', () => {
  37. it('resolves the user layer over the composition entry', async () => {
  38. const bench = await boot()
  39. expect(bench.defaultModel.currentSelection()).toEqual({
  40. provider: 'deepseek-official', model: 'deepseek-v4-flash',
  41. })
  42. await bench.defaultModel.saveSelection({
  43. provider: 'acme-gateway', model: 'acme-large', reasoningEffort: ReasoningEffortId('high'),
  44. })
  45. expect(bench.defaultModel.currentSelection()).toEqual({
  46. provider: 'acme-gateway', model: 'acme-large', reasoningEffort: 'high',
  47. })
  48. await bench.ctx.fiber.dispose()
  49. })
  50. it('clears a stored effort when the saved selection has none', async () => {
  51. const bench = await boot()
  52. await bench.defaultModel.saveSelection({
  53. provider: 'acme-gateway', model: 'acme-large', reasoningEffort: ReasoningEffortId('high'),
  54. })
  55. await bench.defaultModel.saveSelection({ provider: 'acme-gateway', model: 'acme-plain' })
  56. expect(bench.defaultModel.currentSelection()).toEqual({ provider: 'acme-gateway', model: 'acme-plain' })
  57. await bench.ctx.fiber.dispose()
  58. })
  59. it('layers a hand-written partial section over the entry', async () => {
  60. const bench = await boot()
  61. await bench.settingsFiber.ctx.settings.replace(AGENT_DEFAULT_MODEL_SETTINGS_NAMESPACE, {
  62. model: 'deepseek-reasoner',
  63. })
  64. expect(bench.defaultModel.currentSelection()).toEqual({
  65. provider: 'deepseek-official', model: 'deepseek-reasoner',
  66. })
  67. await bench.ctx.fiber.dispose()
  68. })
  69. it('falls back to the composition entry when the settings provider detaches', async () => {
  70. const bench = await boot()
  71. await bench.defaultModel.saveSelection({ provider: 'acme-gateway', model: 'acme-large' })
  72. expect(bench.defaultModel.currentSelection().provider).toBe('acme-gateway')
  73. await bench.settingsFiber.dispose()
  74. expect(bench.defaultModel.currentSelection()).toEqual({
  75. provider: 'deepseek-official', model: 'deepseek-v4-flash',
  76. })
  77. await bench.ctx.fiber.dispose()
  78. })
  79. it('keeps the composition entry when no settings provider is mounted', async () => {
  80. const ctx = new Context()
  81. await ctx.plugin(AgentDefaultModelConfig, { provider: 'p', model: 'm' })
  82. await ctx.agentDefaultModel.saveSelection({ provider: 'other', model: 'other' })
  83. expect(ctx.agentDefaultModel.currentSelection()).toEqual({ provider: 'p', model: 'm' })
  84. await ctx.fiber.dispose()
  85. })
  86. })