openai_endpoint_url.go 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package service
  2. import (
  3. "net/url"
  4. "strings"
  5. )
  6. func buildOpenAIEndpointURL(base string, endpoint string) string {
  7. normalized := strings.TrimSpace(base)
  8. endpoint = "/" + strings.TrimLeft(strings.TrimSpace(endpoint), "/")
  9. relative := strings.TrimPrefix(endpoint, "/v1")
  10. parsed, err := url.Parse(normalized)
  11. if err != nil {
  12. return strings.TrimRight(normalized, "/") + endpoint
  13. }
  14. path := strings.TrimRight(parsed.Path, "/")
  15. if !strings.HasSuffix(path, endpoint) && !strings.HasSuffix(path, relative) {
  16. if openAIBaseURLHasVersionSuffix(path) {
  17. path += relative
  18. } else {
  19. path += endpoint
  20. }
  21. }
  22. parsed.Path = path
  23. parsed.RawPath = ""
  24. parsed.Fragment = ""
  25. return parsed.String()
  26. }
  27. func buildOpenAIResponsesInputTokensURL(base string) string {
  28. return buildOpenAIEndpointURL(base, "/v1/responses/input_tokens")
  29. }
  30. func openAIBaseURLHasVersionSuffix(raw string) bool {
  31. trimmed := strings.TrimSpace(raw)
  32. if trimmed == "" {
  33. return false
  34. }
  35. pathValue := ""
  36. if parsed, err := url.Parse(trimmed); err == nil && parsed.Scheme != "" && parsed.Host != "" {
  37. pathValue = parsed.Path
  38. } else if slash := strings.Index(trimmed, "/"); slash >= 0 {
  39. pathValue = trimmed[slash:]
  40. }
  41. pathValue = strings.TrimRight(pathValue, "/")
  42. if pathValue == "" {
  43. return false
  44. }
  45. lastSlash := strings.LastIndex(pathValue, "/")
  46. segment := pathValue
  47. if lastSlash >= 0 {
  48. segment = pathValue[lastSlash+1:]
  49. }
  50. return isOpenAIAPIVersionSegment(segment)
  51. }
  52. func isOpenAIAPIVersionSegment(segment string) bool {
  53. s := strings.ToLower(strings.TrimSpace(segment))
  54. if len(s) < 2 || s[0] != 'v' || !isASCIIDigit(s[1]) {
  55. return false
  56. }
  57. i := 1
  58. for i < len(s) && isASCIIDigit(s[i]) {
  59. i++
  60. }
  61. if i == len(s) {
  62. return true
  63. }
  64. if s[i] == '.' {
  65. i++
  66. if i == len(s) || !isASCIIDigit(s[i]) {
  67. return false
  68. }
  69. for i < len(s) && isASCIIDigit(s[i]) {
  70. i++
  71. }
  72. return i == len(s)
  73. }
  74. suffix := s[i:]
  75. return strings.HasPrefix(suffix, "alpha") ||
  76. strings.HasPrefix(suffix, "beta") ||
  77. strings.HasPrefix(suffix, "preview")
  78. }
  79. func isASCIIDigit(b byte) bool {
  80. return b >= '0' && b <= '9'
  81. }