You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. from contextlib import contextmanager
  2. from datetime import datetime
  3. from typing import Optional, Union
  4. from urllib.parse import urlparse
  5. from sqlalchemy import select
  6. from extensions.ext_database import db
  7. from models.model import Message
  8. def filter_none_values(data: dict):
  9. new_data = {}
  10. for key, value in data.items():
  11. if value is None:
  12. continue
  13. if isinstance(value, datetime):
  14. new_data[key] = value.isoformat()
  15. else:
  16. new_data[key] = value
  17. return new_data
  18. def get_message_data(message_id: str):
  19. return db.session.scalar(select(Message).where(Message.id == message_id))
  20. @contextmanager
  21. def measure_time():
  22. timing_info = {"start": datetime.now(), "end": None}
  23. try:
  24. yield timing_info
  25. finally:
  26. timing_info["end"] = datetime.now()
  27. def replace_text_with_content(data):
  28. if isinstance(data, dict):
  29. new_data = {}
  30. for key, value in data.items():
  31. if key == "text":
  32. new_data["content"] = value
  33. else:
  34. new_data[key] = replace_text_with_content(value)
  35. return new_data
  36. elif isinstance(data, list):
  37. return [replace_text_with_content(item) for item in data]
  38. else:
  39. return data
  40. def generate_dotted_order(
  41. run_id: str, start_time: Union[str, datetime], parent_dotted_order: Optional[str] = None
  42. ) -> str:
  43. """
  44. generate dotted_order for langsmith
  45. """
  46. start_time = datetime.fromisoformat(start_time) if isinstance(start_time, str) else start_time
  47. timestamp = start_time.strftime("%Y%m%dT%H%M%S%f")[:-3] + "Z"
  48. current_segment = f"{timestamp}{run_id}"
  49. if parent_dotted_order is None:
  50. return current_segment
  51. return f"{parent_dotted_order}.{current_segment}"
  52. def validate_url(url: str, default_url: str, allowed_schemes: tuple = ("https", "http")) -> str:
  53. """
  54. Validate and normalize URL with proper error handling
  55. Args:
  56. url: The URL to validate
  57. default_url: Default URL to use if input is None or empty
  58. allowed_schemes: Tuple of allowed URL schemes (default: https, http)
  59. Returns:
  60. Normalized URL string
  61. Raises:
  62. ValueError: If URL format is invalid or scheme not allowed
  63. """
  64. if not url or url.strip() == "":
  65. return default_url
  66. # Parse URL to validate format
  67. parsed = urlparse(url)
  68. # Check if scheme is allowed
  69. if parsed.scheme not in allowed_schemes:
  70. raise ValueError(f"URL scheme must be one of: {', '.join(allowed_schemes)}")
  71. # Reconstruct URL with only scheme, netloc (removing path, query, fragment)
  72. normalized_url = f"{parsed.scheme}://{parsed.netloc}"
  73. return normalized_url
  74. def validate_url_with_path(url: str, default_url: str, required_suffix: str | None = None) -> str:
  75. """
  76. Validate URL that may include path components
  77. Args:
  78. url: The URL to validate
  79. default_url: Default URL to use if input is None or empty
  80. required_suffix: Optional suffix that URL must end with
  81. Returns:
  82. Validated URL string
  83. Raises:
  84. ValueError: If URL format is invalid or doesn't match required suffix
  85. """
  86. if not url or url.strip() == "":
  87. return default_url
  88. # Parse URL to validate format
  89. parsed = urlparse(url)
  90. # Check if scheme is allowed
  91. if parsed.scheme not in ("https", "http"):
  92. raise ValueError("URL must start with https:// or http://")
  93. # Check required suffix if specified
  94. if required_suffix and not url.endswith(required_suffix):
  95. raise ValueError(f"URL should end with {required_suffix}")
  96. return url
  97. def validate_project_name(project: str, default_name: str) -> str:
  98. """
  99. Validate and normalize project name
  100. Args:
  101. project: Project name to validate
  102. default_name: Default name to use if input is None or empty
  103. Returns:
  104. Normalized project name
  105. """
  106. if not project or project.strip() == "":
  107. return default_name
  108. return project.strip()