llm-context-size.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. import type { LlmConfig, ProviderConfigs, ProviderOverride } from "@/stores/wiki-store"
  2. export const MIN_USER_LLM_CONTEXT_SIZE = 204_800
  3. /** Default declared output ceiling when neither the user nor the preset says
  4. * otherwise. Generous on purpose — it must not silently truncate capable
  5. * models — so presets should carry a real figure wherever one is known. */
  6. const DEFAULT_USER_LLM_MAX_OUTPUT_TOKENS = 131_072
  7. /** Below this an answer is not worth requesting. */
  8. const MIN_USER_LLM_MAX_OUTPUT_TOKENS = 512
  9. /** Highest output any model in the catalog declares (DeepSeek V4: 384K). */
  10. const MAX_USER_LLM_MAX_OUTPUT_TOKENS = 393_216
  11. export function normalizeUserLlmContextSize(value: number | undefined): number {
  12. if (!Number.isFinite(value) || (value as number) <= 0) {
  13. return MIN_USER_LLM_CONTEXT_SIZE
  14. }
  15. return Math.max(MIN_USER_LLM_CONTEXT_SIZE, Math.floor(value as number))
  16. }
  17. /**
  18. * Unlike the context window this has no floor, only a default: a user must be
  19. * able to declare a small ceiling for a model that really does cap out low.
  20. */
  21. export function normalizeUserLlmMaxOutputTokens(value: number | undefined): number {
  22. if (!Number.isFinite(value) || (value as number) <= 0) {
  23. return DEFAULT_USER_LLM_MAX_OUTPUT_TOKENS
  24. }
  25. return Math.max(
  26. MIN_USER_LLM_MAX_OUTPUT_TOKENS,
  27. Math.min(MAX_USER_LLM_MAX_OUTPUT_TOKENS, Math.floor(value as number)),
  28. )
  29. }
  30. export function normalizeUserLlmConfig(config: LlmConfig): LlmConfig {
  31. const maxContextSize = normalizeUserLlmContextSize(config.maxContextSize)
  32. const maxOutputTokens = config.maxOutputTokens === undefined
  33. ? undefined
  34. : normalizeUserLlmMaxOutputTokens(config.maxOutputTokens)
  35. return maxContextSize === config.maxContextSize && maxOutputTokens === config.maxOutputTokens
  36. ? config
  37. : { ...config, maxContextSize, ...(maxOutputTokens === undefined ? {} : { maxOutputTokens }) }
  38. }
  39. export function normalizeProviderOverride(override: ProviderOverride): ProviderOverride {
  40. const maxContextSize = override.maxContextSize === undefined
  41. ? undefined
  42. : normalizeUserLlmContextSize(override.maxContextSize)
  43. const maxOutputTokens = override.maxOutputTokens === undefined
  44. ? undefined
  45. : normalizeUserLlmMaxOutputTokens(override.maxOutputTokens)
  46. if (maxContextSize === override.maxContextSize && maxOutputTokens === override.maxOutputTokens) {
  47. return override
  48. }
  49. return {
  50. ...override,
  51. ...(maxContextSize === undefined ? {} : { maxContextSize }),
  52. ...(maxOutputTokens === undefined ? {} : { maxOutputTokens }),
  53. }
  54. }
  55. export function normalizeProviderConfigs(configs: ProviderConfigs): ProviderConfigs {
  56. let changed = false
  57. const normalized = Object.fromEntries(
  58. Object.entries(configs).map(([id, override]) => {
  59. const next = normalizeProviderOverride(override)
  60. if (next !== override) changed = true
  61. return [id, next]
  62. }),
  63. )
  64. return changed ? normalized : configs
  65. }