model-request.ts 3.4 KB

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