model-request.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. export * as ModelRequest from "./model-request"
  2. import { Effect, Schema } from "effect"
  3. export const Generation = Schema.Struct({
  4. maxTokens: Schema.Number.pipe(Schema.optional),
  5. temperature: Schema.Number.pipe(Schema.optional),
  6. topP: Schema.Number.pipe(Schema.optional),
  7. topK: Schema.Number.pipe(Schema.optional),
  8. frequencyPenalty: Schema.Number.pipe(Schema.optional),
  9. presencePenalty: Schema.Number.pipe(Schema.optional),
  10. seed: Schema.Number.pipe(Schema.optional),
  11. stop: Schema.String.pipe(Schema.Array, Schema.mutable, Schema.optional),
  12. })
  13. export type Generation = typeof Generation.Type
  14. export const Request = Schema.Struct({
  15. headers: Schema.Record(Schema.String, Schema.String),
  16. body: Schema.Record(Schema.String, Schema.Any),
  17. generation: Generation.pipe(
  18. Schema.optionalKey,
  19. Schema.withConstructorDefault(Effect.succeed({})),
  20. Schema.withDecodingDefaultKey(Effect.succeed({})),
  21. ),
  22. options: Schema.Record(Schema.String, Schema.Any).pipe(
  23. Schema.optionalKey,
  24. Schema.withConstructorDefault(Effect.succeed({})),
  25. Schema.withDecodingDefaultKey(Effect.succeed({})),
  26. ),
  27. })
  28. export type Request = typeof Request.Type
  29. interface MutableRequest {
  30. headers: Record<string, string>
  31. body: Record<string, unknown>
  32. generation?: Generation
  33. options?: Record<string, unknown>
  34. }
  35. const generationKeys = new Map<string, keyof Generation>([
  36. ["maxOutputTokens", "maxTokens"],
  37. ["maxTokens", "maxTokens"],
  38. ["temperature", "temperature"],
  39. ["topP", "topP"],
  40. ["topK", "topK"],
  41. ["frequencyPenalty", "frequencyPenalty"],
  42. ["presencePenalty", "presencePenalty"],
  43. ["seed", "seed"],
  44. ["stopSequences", "stop"],
  45. ["stop", "stop"],
  46. ])
  47. interface Profile {
  48. readonly namespace: string
  49. readonly semantics: ReadonlyMap<string, string>
  50. }
  51. const profiles = new Map<string, Profile>([
  52. [
  53. "@ai-sdk/openai",
  54. {
  55. namespace: "openai",
  56. semantics: new Map([
  57. ["store", "store"],
  58. ["promptCacheKey", "promptCacheKey"],
  59. ["reasoningEffort", "reasoningEffort"],
  60. ["reasoningSummary", "reasoningSummary"],
  61. ["include", "include"],
  62. ["textVerbosity", "textVerbosity"],
  63. ["serviceTier", "serviceTier"],
  64. ["service_tier", "serviceTier"],
  65. ]),
  66. },
  67. ],
  68. [
  69. "@ai-sdk/openai-compatible",
  70. {
  71. namespace: "openai",
  72. semantics: new Map([
  73. ["store", "store"],
  74. ["promptCacheKey", "promptCacheKey"],
  75. ["reasoningEffort", "reasoningEffort"],
  76. ["reasoning_effort", "reasoningEffort"],
  77. ]),
  78. },
  79. ],
  80. ["@ai-sdk/anthropic", { namespace: "anthropic", semantics: new Map([["thinking", "thinking"]]) }],
  81. ])
  82. export const namespace = (packageName: string) => profiles.get(packageName)?.namespace
  83. export const merge = (base: Request, override: Partial<Request>) => ({
  84. headers: { ...base.headers, ...override.headers },
  85. body: { ...base.body, ...override.body },
  86. generation: { ...base.generation, ...override.generation },
  87. options: { ...base.options, ...override.options },
  88. })
  89. export const assign = (target: MutableRequest, override: Partial<Request>) => {
  90. Object.assign(target.headers, override.headers)
  91. Object.assign(target.body, override.body)
  92. Object.assign((target.generation ??= {}), override.generation)
  93. Object.assign((target.options ??= {}), override.options)
  94. }
  95. /** Partitions AI-SDK-shaped request options before they enter the Catalog. */
  96. export function normalizeAiSdkOptions(packageName: string | undefined, input: Readonly<Record<string, unknown>>) {
  97. const generation: Record<string, number | ReadonlyArray<string>> = {}
  98. const options: Record<string, unknown> = {}
  99. const body: Record<string, unknown> = {}
  100. const semantics = profiles.get(packageName ?? "")?.semantics
  101. for (const [key, value] of Object.entries(input)) {
  102. const generationKey = generationKeys.get(key)
  103. if (generationKey === "stop" && Array.isArray(value) && value.every((item) => typeof item === "string"))
  104. generation[generationKey] = value
  105. else if (generationKey !== undefined && generationKey !== "stop" && typeof value === "number")
  106. generation[generationKey] = value
  107. else if (semantics?.has(key)) options[semantics.get(key)!] = value
  108. else body[key] = value
  109. }
  110. return { generation, options, body }
  111. }