railtracks.llm
1from . import retries 2from .content import ToolCall, ToolResponse 3from .history import MessageHistory 4from .message import AssistantMessage, Message, SystemMessage, ToolMessage, UserMessage 5from .model import ModelBase 6from .models import ( 7 AnthropicLLM, 8 AzureAILLM, 9 CohereLLM, 10 GeminiLLM, 11 HuggingFaceLLM, 12 OllamaLLM, 13 OpenAICompatibleProvider, 14 OpenAILLM, 15 PortKeyLLM, 16 # TelusLLM, 17) 18from .models._model_exception_base import ( 19 FunctionCallingNotSupportedError, 20 ModelError, 21 ModelNotFoundError, 22 MutuallyExclusiveHyperparametersError, 23 UnsupportedHyperparameterError, 24) 25from .providers import ModelProvider 26from .tools import ( 27 ArrayParameter, 28 ObjectParameter, 29 Parameter, 30 RefParameter, 31 Tool, 32 UnionParameter, 33) 34 35__all__ = [ 36 "ModelBase", 37 "ModelError", 38 "ModelNotFoundError", 39 "FunctionCallingNotSupportedError", 40 "UnsupportedHyperparameterError", 41 "MutuallyExclusiveHyperparametersError", 42 "ToolCall", 43 "ToolResponse", 44 "UserMessage", 45 "SystemMessage", 46 "AssistantMessage", 47 "Message", 48 "ToolMessage", 49 "MessageHistory", 50 "ModelProvider", 51 "Tool", 52 "AnthropicLLM", 53 "AzureAILLM", 54 "CohereLLM", 55 "HuggingFaceLLM", 56 "OpenAILLM", 57 "GeminiLLM", 58 "OllamaLLM", 59 "AzureAILLM", 60 "GeminiLLM", 61 # "TelusLLM", 62 "PortKeyLLM", 63 "OpenAICompatibleProvider", 64 "CohereLLM", 65 # Parameter types 66 "Parameter", 67 "UnionParameter", 68 "ArrayParameter", 69 "ObjectParameter", 70 "RefParameter", 71 "retries", 72]
32class ModelBase(ABC, Generic[_TStream]): 33 """ 34 A simple base that represents the behavior of a model that can be used for chat, structured interactions, and streaming. 35 36 The base class allows for the insertion of hooks that can modify the messages before they are sent to the model, 37 response after they are received, and map exceptions that may occur during the interaction. 38 39 All the hooks are optional and can be added or removed as needed. 40 """ 41 42 def __init__( 43 self, 44 __pre_hooks: List[Callable[[MessageHistory], MessageHistory]] | None = None, 45 __post_hooks: List[Callable[[MessageHistory, Response], Response]] 46 | None = None, 47 __exception_hooks: List[Callable[[MessageHistory, Exception], None]] 48 | None = None, 49 stream: _TStream = False, 50 retry_approach: RetryApproach | None = None, 51 ): 52 if __pre_hooks is None: 53 pre_hooks: List[Callable[[MessageHistory], MessageHistory]] = [] 54 else: 55 pre_hooks = __pre_hooks 56 57 if __post_hooks is None: 58 post_hooks: List[Callable[[MessageHistory, Response], Response]] = [] 59 else: 60 post_hooks = __post_hooks 61 62 if __exception_hooks is None: 63 exception_hooks: List[Callable[[MessageHistory, Exception], None]] = [] 64 else: 65 exception_hooks = __exception_hooks 66 67 self._pre_hooks = pre_hooks 68 self._post_hooks = post_hooks 69 self._exception_hooks = exception_hooks 70 self.stream = stream 71 self.retry_approach = retry_approach 72 73 def add_pre_hook(self, hook: Callable[[MessageHistory], MessageHistory]) -> None: 74 """Adds a pre-hook to modify messages before sending them to the model.""" 75 self._pre_hooks.append(hook) 76 77 def add_post_hook( 78 self, hook: Callable[[MessageHistory, Response], Response] 79 ) -> None: 80 """Adds a post-hook to modify the response after receiving it from the model.""" 81 self._post_hooks.append(hook) 82 83 def add_exception_hook( 84 self, hook: Callable[[MessageHistory, Exception], None] 85 ) -> None: 86 """Adds an exception hook to handle exceptions during model interactions.""" 87 self._exception_hooks.append(hook) 88 89 def remove_pre_hooks(self) -> None: 90 """Removes all of the hooks that modify messages before sending them to the model.""" 91 self._pre_hooks = [] 92 93 def remove_post_hooks(self) -> None: 94 """Removes all of the hooks that modify the response after receiving it from the model.""" 95 self._post_hooks = [] 96 97 def remove_exception_hooks(self) -> None: 98 """Removes all of the hooks that handle exceptions during model interactions.""" 99 self._exception_hooks = [] 100 101 @abstractmethod 102 def model_name(self) -> str: 103 """ 104 Returns the name of the model being used. 105 106 It can be treated as unique identifier for the model when paired with the `model_type`. 107 """ 108 pass 109 110 @abstractmethod 111 def model_provider(self) -> ModelProvider: 112 """The name of the provider of this model (The Company that owns the model)""" 113 pass 114 115 @classmethod 116 @abstractmethod 117 def model_gateway(cls) -> ModelProvider: 118 """ 119 Gets the API distrubutor of the model. Note nessecarily the same as the model itself. 120 121 E.g. if you are calling openai LLM through Azure AI foundry 122 """ 123 pass 124 125 def _run_pre_hooks(self, message_history: MessageHistory) -> MessageHistory: 126 """Runs all pre-hooks on the provided message history.""" 127 for hook in self._pre_hooks: 128 message_history = hook(message_history) 129 return message_history 130 131 def _run_post_hooks( 132 self, message_history: MessageHistory, result: Response 133 ) -> Response: 134 """Runs all post-hooks on the provided message history and result.""" 135 for hook in self._post_hooks: 136 result = hook(message_history, result) 137 return result 138 139 def _run_exception_hooks( 140 self, message_history: MessageHistory, exception: Exception 141 ) -> None: 142 """Runs all exception hooks on the provided message history and exception.""" 143 for hook in self._exception_hooks: 144 hook(message_history, exception) 145 146 def generator_wrapper( 147 self, 148 generator: Generator[str | Response, None, Response], 149 message_history: MessageHistory, 150 ) -> Generator[str | Response, None, Response]: 151 new_response: Response | None = None 152 for g in generator: 153 if isinstance(g, Response): 154 g.message_info 155 new_response = self._run_post_hooks(message_history, g) 156 yield new_response 157 158 yield g 159 160 assert new_response is not None, ( 161 "The generator did not yield a final Response object so nothing could be done." 162 ) 163 164 return new_response 165 166 @overload 167 def chat(self: ModelBase[Literal[False]], messages: MessageHistory) -> Response: 168 pass 169 170 @overload 171 def chat( 172 self: ModelBase[Literal[True]], messages: MessageHistory 173 ) -> Generator[str | Response, None, Response]: 174 pass 175 176 def chat( 177 self, messages: MessageHistory 178 ) -> Response | Generator[str | Response, None, Response]: 179 """Chat with the model using the provided messages.""" 180 181 messages = self._run_pre_hooks(messages) 182 183 try: 184 response = self._chat(messages) 185 except Exception as e: 186 self._run_exception_hooks(messages, e) 187 raise e 188 189 if isinstance(response, Generator): 190 return self.generator_wrapper(response, messages) 191 192 response = self._run_post_hooks(messages, response) 193 return response 194 195 @overload 196 async def achat( 197 self: ModelBase[Literal[False]], messages: MessageHistory 198 ) -> Response: 199 pass 200 201 @overload 202 async def achat( 203 self: ModelBase[Literal[True]], messages: MessageHistory 204 ) -> Generator[str | Response, None, Response]: 205 pass 206 207 async def achat(self, messages: MessageHistory): 208 """Asynchronous chat with the model using the provided messages.""" 209 messages = self._run_pre_hooks(messages) 210 211 try: 212 response = await self._achat(messages) 213 except Exception as e: 214 self._run_exception_hooks(messages, e) 215 raise e 216 217 if isinstance(response, Generator): 218 return self.generator_wrapper(response, messages) 219 220 response = self._run_post_hooks(messages, response) 221 222 return response 223 224 @overload 225 def structured( 226 self: ModelBase[Literal[False]], 227 messages: MessageHistory, 228 schema: Type[BaseModel], 229 ) -> Response: 230 pass 231 232 @overload 233 def structured( 234 self: ModelBase[Literal[True]], 235 messages: MessageHistory, 236 schema: Type[BaseModel], 237 ) -> Generator[str | Response, None, Response]: 238 pass 239 240 def structured(self, messages: MessageHistory, schema: Type[BaseModel]): 241 """Structured interaction with the model using the provided messages and output_schema.""" 242 messages = self._run_pre_hooks(messages) 243 244 try: 245 response = self._structured(messages, schema) 246 except Exception as e: 247 self._run_exception_hooks(messages, e) 248 raise e 249 250 if isinstance(response, Generator): 251 return self.generator_wrapper(response, messages) 252 253 response = self._run_post_hooks(messages, response) 254 255 return response 256 257 @overload 258 async def astructured( 259 self: ModelBase[Literal[False]], 260 messages: MessageHistory, 261 schema: Type[BaseModel], 262 ) -> Response: 263 pass 264 265 @overload 266 async def astructured( 267 self: ModelBase[Literal[True]], 268 messages: MessageHistory, 269 schema: Type[BaseModel], 270 ) -> Generator[str | Response, None, Response]: 271 pass 272 273 async def astructured(self, messages: MessageHistory, schema: Type[BaseModel]): 274 """Asynchronous structured interaction with the model using the provided messages and output_schema.""" 275 messages = self._run_pre_hooks(messages) 276 277 try: 278 response = await self._astructured(messages, schema) 279 except Exception as e: 280 self._run_exception_hooks(messages, e) 281 raise e 282 283 if isinstance(response, Generator): 284 return self.generator_wrapper(response, messages) 285 286 response = self._run_post_hooks(messages, response) 287 288 return response 289 290 @overload 291 def chat_with_tools( 292 self: ModelBase[Literal[False]], messages: MessageHistory, tools: List[Tool] 293 ) -> Response: 294 pass 295 296 @overload 297 def chat_with_tools( 298 self: ModelBase[Literal[True]], messages: MessageHistory, tools: List[Tool] 299 ) -> Generator[str | Response, None, Response]: 300 pass 301 302 def chat_with_tools(self, messages: MessageHistory, tools: List[Tool]): 303 """Chat with the model using the provided messages and tools.""" 304 messages = self._run_pre_hooks(messages) 305 306 try: 307 response = self._chat_with_tools(messages, tools) 308 except Exception as e: 309 self._run_exception_hooks(messages, e) 310 raise e 311 312 if isinstance(response, Generator): 313 return self.generator_wrapper(response, messages) 314 315 response = self._run_post_hooks(messages, response) 316 return response 317 318 @overload 319 async def achat_with_tools( 320 self: ModelBase[Literal[False]], messages: MessageHistory, tools: List[Tool] 321 ) -> Response: 322 pass 323 324 @overload 325 async def achat_with_tools( 326 self: ModelBase[Literal[True]], messages: MessageHistory, tools: List[Tool] 327 ) -> Generator[str | Response, None, Response]: 328 pass 329 330 async def achat_with_tools(self, messages: MessageHistory, tools: List[Tool]): 331 """Asynchronous chat with the model using the provided messages and tools.""" 332 messages = self._run_pre_hooks(messages) 333 334 try: 335 response = await self._achat_with_tools(messages, tools) 336 except Exception as e: 337 self._run_exception_hooks(messages, e) 338 raise e 339 340 if isinstance(response, Generator): 341 return self.generator_wrapper(response, messages) 342 343 response = self._run_post_hooks(messages, response) 344 345 return response 346 347 @abstractmethod 348 def _chat( 349 self, messages: MessageHistory 350 ) -> Response | Generator[str | Response, None, Response]: 351 pass 352 353 @abstractmethod 354 def _structured( 355 self, messages: MessageHistory, schema: Type[BaseModel] 356 ) -> Response | Generator[str | Response, None, Response]: 357 pass 358 359 @abstractmethod 360 def _chat_with_tools( 361 self, messages: MessageHistory, tools: List[Tool] 362 ) -> Response | Generator[str | Response, None, Response]: 363 pass 364 365 @abstractmethod 366 async def _achat( 367 self, messages: MessageHistory 368 ) -> Response | AsyncGenerator[str | Response, None]: 369 pass 370 371 @abstractmethod 372 async def _astructured( 373 self, 374 messages: MessageHistory, 375 schema: Type[BaseModel], 376 ) -> Response | AsyncGenerator[str | Response, None]: 377 pass 378 379 @abstractmethod 380 async def _achat_with_tools( 381 self, messages: MessageHistory, tools: List[Tool] 382 ) -> Response | AsyncGenerator[str | Response, None]: 383 pass
A simple base that represents the behavior of a model that can be used for chat, structured interactions, and streaming.
The base class allows for the insertion of hooks that can modify the messages before they are sent to the model, response after they are received, and map exceptions that may occur during the interaction.
All the hooks are optional and can be added or removed as needed.
73 def add_pre_hook(self, hook: Callable[[MessageHistory], MessageHistory]) -> None: 74 """Adds a pre-hook to modify messages before sending them to the model.""" 75 self._pre_hooks.append(hook)
Adds a pre-hook to modify messages before sending them to the model.
77 def add_post_hook( 78 self, hook: Callable[[MessageHistory, Response], Response] 79 ) -> None: 80 """Adds a post-hook to modify the response after receiving it from the model.""" 81 self._post_hooks.append(hook)
Adds a post-hook to modify the response after receiving it from the model.
83 def add_exception_hook( 84 self, hook: Callable[[MessageHistory, Exception], None] 85 ) -> None: 86 """Adds an exception hook to handle exceptions during model interactions.""" 87 self._exception_hooks.append(hook)
Adds an exception hook to handle exceptions during model interactions.
89 def remove_pre_hooks(self) -> None: 90 """Removes all of the hooks that modify messages before sending them to the model.""" 91 self._pre_hooks = []
Removes all of the hooks that modify messages before sending them to the model.
93 def remove_post_hooks(self) -> None: 94 """Removes all of the hooks that modify the response after receiving it from the model.""" 95 self._post_hooks = []
Removes all of the hooks that modify the response after receiving it from the model.
97 def remove_exception_hooks(self) -> None: 98 """Removes all of the hooks that handle exceptions during model interactions.""" 99 self._exception_hooks = []
Removes all of the hooks that handle exceptions during model interactions.
101 @abstractmethod 102 def model_name(self) -> str: 103 """ 104 Returns the name of the model being used. 105 106 It can be treated as unique identifier for the model when paired with the `model_type`. 107 """ 108 pass
Returns the name of the model being used.
It can be treated as unique identifier for the model when paired with the model_type.
110 @abstractmethod 111 def model_provider(self) -> ModelProvider: 112 """The name of the provider of this model (The Company that owns the model)""" 113 pass
The name of the provider of this model (The Company that owns the model)
115 @classmethod 116 @abstractmethod 117 def model_gateway(cls) -> ModelProvider: 118 """ 119 Gets the API distrubutor of the model. Note nessecarily the same as the model itself. 120 121 E.g. if you are calling openai LLM through Azure AI foundry 122 """ 123 pass
Gets the API distrubutor of the model. Note nessecarily the same as the model itself.
E.g. if you are calling openai LLM through Azure AI foundry
146 def generator_wrapper( 147 self, 148 generator: Generator[str | Response, None, Response], 149 message_history: MessageHistory, 150 ) -> Generator[str | Response, None, Response]: 151 new_response: Response | None = None 152 for g in generator: 153 if isinstance(g, Response): 154 g.message_info 155 new_response = self._run_post_hooks(message_history, g) 156 yield new_response 157 158 yield g 159 160 assert new_response is not None, ( 161 "The generator did not yield a final Response object so nothing could be done." 162 ) 163 164 return new_response
176 def chat( 177 self, messages: MessageHistory 178 ) -> Response | Generator[str | Response, None, Response]: 179 """Chat with the model using the provided messages.""" 180 181 messages = self._run_pre_hooks(messages) 182 183 try: 184 response = self._chat(messages) 185 except Exception as e: 186 self._run_exception_hooks(messages, e) 187 raise e 188 189 if isinstance(response, Generator): 190 return self.generator_wrapper(response, messages) 191 192 response = self._run_post_hooks(messages, response) 193 return response
Chat with the model using the provided messages.
207 async def achat(self, messages: MessageHistory): 208 """Asynchronous chat with the model using the provided messages.""" 209 messages = self._run_pre_hooks(messages) 210 211 try: 212 response = await self._achat(messages) 213 except Exception as e: 214 self._run_exception_hooks(messages, e) 215 raise e 216 217 if isinstance(response, Generator): 218 return self.generator_wrapper(response, messages) 219 220 response = self._run_post_hooks(messages, response) 221 222 return response
Asynchronous chat with the model using the provided messages.
240 def structured(self, messages: MessageHistory, schema: Type[BaseModel]): 241 """Structured interaction with the model using the provided messages and output_schema.""" 242 messages = self._run_pre_hooks(messages) 243 244 try: 245 response = self._structured(messages, schema) 246 except Exception as e: 247 self._run_exception_hooks(messages, e) 248 raise e 249 250 if isinstance(response, Generator): 251 return self.generator_wrapper(response, messages) 252 253 response = self._run_post_hooks(messages, response) 254 255 return response
Structured interaction with the model using the provided messages and output_schema.
273 async def astructured(self, messages: MessageHistory, schema: Type[BaseModel]): 274 """Asynchronous structured interaction with the model using the provided messages and output_schema.""" 275 messages = self._run_pre_hooks(messages) 276 277 try: 278 response = await self._astructured(messages, schema) 279 except Exception as e: 280 self._run_exception_hooks(messages, e) 281 raise e 282 283 if isinstance(response, Generator): 284 return self.generator_wrapper(response, messages) 285 286 response = self._run_post_hooks(messages, response) 287 288 return response
Asynchronous structured interaction with the model using the provided messages and output_schema.
302 def chat_with_tools(self, messages: MessageHistory, tools: List[Tool]): 303 """Chat with the model using the provided messages and tools.""" 304 messages = self._run_pre_hooks(messages) 305 306 try: 307 response = self._chat_with_tools(messages, tools) 308 except Exception as e: 309 self._run_exception_hooks(messages, e) 310 raise e 311 312 if isinstance(response, Generator): 313 return self.generator_wrapper(response, messages) 314 315 response = self._run_post_hooks(messages, response) 316 return response
Chat with the model using the provided messages and tools.
330 async def achat_with_tools(self, messages: MessageHistory, tools: List[Tool]): 331 """Asynchronous chat with the model using the provided messages and tools.""" 332 messages = self._run_pre_hooks(messages) 333 334 try: 335 response = await self._achat_with_tools(messages, tools) 336 except Exception as e: 337 self._run_exception_hooks(messages, e) 338 raise e 339 340 if isinstance(response, Generator): 341 return self.generator_wrapper(response, messages) 342 343 response = self._run_post_hooks(messages, response) 344 345 return response
Asynchronous chat with the model using the provided messages and tools.
6class ModelError(RTLLMError): 7 """ 8 Any Large Language Model (LLM) error. 9 """ 10 11 def __init__( 12 self, 13 reason: str, 14 message_history: MessageHistory = None, 15 ): 16 self.reason = reason 17 self.message_history = message_history 18 19 message = f"{self._color('Failure reason: ', self.BOLD_RED)}{self._color(reason, self.RED)}" 20 super().__init__(message) 21 22 def __str__(self): 23 base = super().__str__() 24 details = [] 25 if self.message_history: 26 mh_str = str(self.message_history) 27 indented_mh = "\n".join( 28 " " + line for line in mh_str.splitlines() 29 ) # 2 indents (2-spaces) per indent 30 details.append( 31 self._color("Message History:\n", self.BOLD_GREEN) 32 + self._color(indented_mh, self.GREEN) 33 ) 34 if details: 35 notes_str = ( 36 "\n" 37 + self._color("Details:\n", self.BOLD_GREEN) 38 + "\n".join(f" {d}" for d in details) 39 ) 40 return f"\n{self._color(base, self.RED)}{notes_str}" 41 return self._color(base, self.RED)
Any Large Language Model (LLM) error.
44class ModelNotFoundError(RTLLMError): 45 def __init__(self, reason: str, notes: list[str] = None): 46 self.reason = reason 47 self.notes = notes or [] 48 super().__init__(reason) 49 50 def __str__(self): 51 base = super().__str__() 52 if self.notes: 53 notes_str = ( 54 "\n" 55 + self._color("Tips to debug:\n", self.GREEN) 56 + "\n".join(self._color(f"- {note}", self.GREEN) for note in self.notes) 57 ) 58 return f"\n{self._color(base, self.RED)}{notes_str}" 59 return self._color(base, self.RED)
A simple base class for all LLM Exceptions to inherit from.
62class FunctionCallingNotSupportedError(ModelError): 63 """Error raised when a model does not support function calling.""" 64 65 def __init__(self, model_name: str): 66 super().__init__( 67 reason=f"Model {model_name} does not support function calling. Chat with tools is not supported." 68 )
Error raised when a model does not support function calling.
71class UnsupportedHyperparameterError(ModelError): 72 """Error raised when a model does not support a given common LLM hyperparameter.""" 73 74 def __init__(self, model_name: str, hyperparameter: str, value): 75 super().__init__( 76 reason=( 77 f"Model {model_name} does not support '{hyperparameter}' " 78 f"(got {hyperparameter}={value!r})." 79 ) 80 )
Error raised when a model does not support a given common LLM hyperparameter.
83class MutuallyExclusiveHyperparametersError(ModelError): 84 """Error raised when two or more common hyperparameters cannot be combined for 85 this model.""" 86 87 def __init__(self, model_name: str, hyperparameters: list[str], values: dict): 88 joined = " and ".join(f"'{p}'" for p in hyperparameters) 89 super().__init__( 90 reason=( 91 f"Model {model_name} does not support specifying {joined} together " 92 f"(got {values!r}). Use only one." 93 ) 94 )
Error raised when two or more common hyperparameters cannot be combined for this model.
12class ToolCall(BaseModel): 13 """ 14 A simple model object that represents a tool call. 15 16 This simple model represents a moment when a tool is called. 17 """ 18 19 identifier: str = Field(description="The identifier attatched to this tool call.") 20 name: str = Field(description="The name of the tool being called.") 21 arguments: Dict[str, Any] = Field( 22 description="The arguments provided as input to the tool." 23 ) 24 25 def __str__(self): 26 return f"{self.name}({self.arguments})"
A simple model object that represents a tool call.
This simple model represents a moment when a tool is called.
29class ToolResponse(BaseModel): 30 """ 31 A simple model object that represents a tool response. 32 33 This simple model should be used when adding a response to a tool. 34 """ 35 36 identifier: str = Field( 37 description="The identifier attached to this tool response. This should match the identifier of the tool call." 38 ) 39 name: str = Field(description="The name of the tool that generated this response.") 40 result: AnyStr = Field(description="The result of the tool call.") 41 42 def __str__(self): 43 return f"{self.name} -> {self.result}"
A simple model object that represents a tool response.
This simple model should be used when adding a response to a tool.
279class UserMessage(_StringOnlyContent[Role.user]): 280 """ 281 Note that we only support string input 282 283 Args: 284 content: The content of the user message. 285 attachment: The file attachment(s) for the user message. Can be a single string or a list of strings, 286 containing file paths, URLs, or data URIs. Defaults to None. 287 inject_prompt: Whether to inject prompt with context variables. Defaults to True. 288 trust_urls: Allow in-process fetch for URL attachments. Defaults to False. 289 When False, `.pdf` URLs raise and unknown-extension URLs are 290 handed to the provider unprobed (as `image_url`). When True, 291 `.pdf` URLs are downloaded and embedded as base64, and 292 unknown-extension URLs are HEAD-probed to detect PDFs. 293 Set True only when every URL in `attachment` is 294 developer-controlled — end-user-supplied URLs are an SSRF 295 surface once their bytes are fetched in-process. 296 attachment_timeout: Per-request timeout in seconds for the HEAD probe 297 and the in-process PDF download. Defaults to 10. Only 298 applies when `trust_urls=True`. Raise this for large PDFs 299 over slow links. 300 """ 301 302 def __init__( 303 self, 304 content: str | None = None, 305 attachment: str | list[str] | None = None, 306 inject_prompt: bool = True, 307 trust_urls: bool = False, 308 attachment_timeout: float = 10.0, 309 ): 310 if attachment is not None: 311 if isinstance(attachment, list): 312 self.attachment = [ 313 Attachment( 314 att, 315 trust_urls=trust_urls, 316 attachment_timeout=attachment_timeout, 317 ) 318 for att in attachment 319 ] 320 else: 321 self.attachment = [ 322 Attachment( 323 attachment, 324 trust_urls=trust_urls, 325 attachment_timeout=attachment_timeout, 326 ) 327 ] 328 329 if content is None: 330 logger.warning( 331 "UserMessage initialized without content, setting to empty string." 332 ) 333 content = "" 334 else: 335 self.attachment = None 336 337 if content is None: 338 raise ValueError( 339 "UserMessage must have content if no attachment is provided." 340 ) 341 super().__init__(content=content, role=Role.user, inject_prompt=inject_prompt)
Note that we only support string input
Arguments:
- content: The content of the user message.
- attachment: The file attachment(s) for the user message. Can be a single string or a list of strings, containing file paths, URLs, or data URIs. Defaults to None.
- inject_prompt: Whether to inject prompt with context variables. Defaults to True.
- trust_urls: Allow in-process fetch for URL attachments. Defaults to False.
When False,
.pdfURLs raise and unknown-extension URLs are handed to the provider unprobed (asimage_url). When True,.pdfURLs are downloaded and embedded as base64, and unknown-extension URLs are HEAD-probed to detect PDFs. Set True only when every URL inattachmentis developer-controlled — end-user-supplied URLs are an SSRF surface once their bytes are fetched in-process. - attachment_timeout: Per-request timeout in seconds for the HEAD probe
and the in-process PDF download. Defaults to 10. Only
applies when
trust_urls=True. Raise this for large PDFs over slow links.
302 def __init__( 303 self, 304 content: str | None = None, 305 attachment: str | list[str] | None = None, 306 inject_prompt: bool = True, 307 trust_urls: bool = False, 308 attachment_timeout: float = 10.0, 309 ): 310 if attachment is not None: 311 if isinstance(attachment, list): 312 self.attachment = [ 313 Attachment( 314 att, 315 trust_urls=trust_urls, 316 attachment_timeout=attachment_timeout, 317 ) 318 for att in attachment 319 ] 320 else: 321 self.attachment = [ 322 Attachment( 323 attachment, 324 trust_urls=trust_urls, 325 attachment_timeout=attachment_timeout, 326 ) 327 ] 328 329 if content is None: 330 logger.warning( 331 "UserMessage initialized without content, setting to empty string." 332 ) 333 content = "" 334 else: 335 self.attachment = None 336 337 if content is None: 338 raise ValueError( 339 "UserMessage must have content if no attachment is provided." 340 ) 341 super().__init__(content=content, role=Role.user, inject_prompt=inject_prompt)
A simple class that represents a message that an LLM can read.
Arguments:
- content: The content of the message. It can take on any of the following types:
- str: A simple string message.
- List[ToolCall]: A list of tool calls.
- ToolResponse: A tool response.
- BaseModel: A custom base model object.
- Stream: A stream object with a final_message and a generator.
- role: The role of the message (assistant, user, system, tool, etc.).
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
344class SystemMessage(_StringOnlyContent[Role.system]): 345 """ 346 A simple class that represents a system message. 347 348 Args: 349 content (str): The content of the system message. 350 inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True. 351 """ 352 353 def __init__(self, content: str, inject_prompt: bool = True): 354 super().__init__(content=content, role=Role.system, inject_prompt=inject_prompt)
A simple class that represents a system message.
Arguments:
- content (str): The content of the system message.
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
353 def __init__(self, content: str, inject_prompt: bool = True): 354 super().__init__(content=content, role=Role.system, inject_prompt=inject_prompt)
A simple class that represents a message that an LLM can read.
Arguments:
- content: The content of the message. It can take on any of the following types:
- str: A simple string message.
- List[ToolCall]: A list of tool calls.
- ToolResponse: A tool response.
- BaseModel: A custom base model object.
- Stream: A stream object with a final_message and a generator.
- role: The role of the message (assistant, user, system, tool, etc.).
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
357class AssistantMessage(Message[_T, Role.assistant], Generic[_T]): 358 """ 359 A simple class that represents a message from the assistant. 360 361 Args: 362 content (_T): The content of the assistant message. 363 inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True. 364 """ 365 366 def __init__(self, content: _T, inject_prompt: bool = True): 367 super().__init__( 368 content=content, role=Role.assistant, inject_prompt=inject_prompt 369 ) 370 371 # Optionally stores the raw litellm message object so providers that 372 # attach extra metadata (e.g. Gemini thought_signature) can round-trip 373 # it back without any manual reconstruction. 374 self.raw_litellm_message = None
A simple class that represents a message from the assistant.
Arguments:
- content (_T): The content of the assistant message.
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
366 def __init__(self, content: _T, inject_prompt: bool = True): 367 super().__init__( 368 content=content, role=Role.assistant, inject_prompt=inject_prompt 369 ) 370 371 # Optionally stores the raw litellm message object so providers that 372 # attach extra metadata (e.g. Gemini thought_signature) can round-trip 373 # it back without any manual reconstruction. 374 self.raw_litellm_message = None
A simple class that represents a message that an LLM can read.
Arguments:
- content: The content of the message. It can take on any of the following types:
- str: A simple string message.
- List[ToolCall]: A list of tool calls.
- ToolResponse: A tool response.
- BaseModel: A custom base model object.
- Stream: A stream object with a final_message and a generator.
- role: The role of the message (assistant, user, system, tool, etc.).
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
186class Message(Generic[_T, _TRole]): 187 """ 188 A base class that represents a message that an LLM can read. 189 190 Note the content may take on a variety of allowable types. 191 """ 192 193 def __init__( 194 self, 195 content: _T, 196 role: _TRole, 197 inject_prompt: bool = True, 198 ): 199 """ 200 A simple class that represents a message that an LLM can read. 201 202 Args: 203 content: The content of the message. It can take on any of the following types: 204 - str: A simple string message. 205 - List[ToolCall]: A list of tool calls. 206 - ToolResponse: A tool response. 207 - BaseModel: A custom base model object. 208 - Stream: A stream object with a final_message and a generator. 209 role: The role of the message (assistant, user, system, tool, etc.). 210 inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True. 211 """ 212 assert isinstance(role, Role) 213 self.validate_content(content) 214 self._content = content 215 self._role = role 216 self._inject_prompt = inject_prompt 217 218 @classmethod 219 def validate_content(cls, content: _T): 220 pass 221 222 @property 223 def content(self) -> _T: 224 """Collects the content of the message.""" 225 return self._content 226 227 @property 228 def role(self) -> _TRole: 229 """Collects the role of the message.""" 230 return self._role 231 232 @property 233 def inject_prompt(self) -> bool: 234 """ 235 A boolean that indicates whether this message should be injected into from context. 236 """ 237 return self._inject_prompt 238 239 @inject_prompt.setter 240 def inject_prompt(self, value: bool): 241 """ 242 Sets the inject_prompt property. 243 """ 244 self._inject_prompt = value 245 246 def __str__(self): 247 return f"{self.role.value}: {self.content}" 248 249 def __repr__(self): 250 return str(self) 251 252 @property 253 def tool_calls(self): 254 """Gets the tool calls attached to this message, if any. If there are none return and empty list.""" 255 tools: list[ToolCall] = [] 256 if isinstance(self.content, list): 257 tools.extend(deepcopy(self.content)) 258 259 return tools
A base class that represents a message that an LLM can read.
Note the content may take on a variety of allowable types.
193 def __init__( 194 self, 195 content: _T, 196 role: _TRole, 197 inject_prompt: bool = True, 198 ): 199 """ 200 A simple class that represents a message that an LLM can read. 201 202 Args: 203 content: The content of the message. It can take on any of the following types: 204 - str: A simple string message. 205 - List[ToolCall]: A list of tool calls. 206 - ToolResponse: A tool response. 207 - BaseModel: A custom base model object. 208 - Stream: A stream object with a final_message and a generator. 209 role: The role of the message (assistant, user, system, tool, etc.). 210 inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True. 211 """ 212 assert isinstance(role, Role) 213 self.validate_content(content) 214 self._content = content 215 self._role = role 216 self._inject_prompt = inject_prompt
A simple class that represents a message that an LLM can read.
Arguments:
- content: The content of the message. It can take on any of the following types:
- str: A simple string message.
- List[ToolCall]: A list of tool calls.
- ToolResponse: A tool response.
- BaseModel: A custom base model object.
- Stream: A stream object with a final_message and a generator.
- role: The role of the message (assistant, user, system, tool, etc.).
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
222 @property 223 def content(self) -> _T: 224 """Collects the content of the message.""" 225 return self._content
Collects the content of the message.
227 @property 228 def role(self) -> _TRole: 229 """Collects the role of the message.""" 230 return self._role
Collects the role of the message.
232 @property 233 def inject_prompt(self) -> bool: 234 """ 235 A boolean that indicates whether this message should be injected into from context. 236 """ 237 return self._inject_prompt
A boolean that indicates whether this message should be injected into from context.
252 @property 253 def tool_calls(self): 254 """Gets the tool calls attached to this message, if any. If there are none return and empty list.""" 255 tools: list[ToolCall] = [] 256 if isinstance(self.content, list): 257 tools.extend(deepcopy(self.content)) 258 259 return tools
Gets the tool calls attached to this message, if any. If there are none return and empty list.
378class ToolMessage(Message[ToolResponse, Role.tool]): 379 """ 380 A simple class that represents a message that is a tool call answer. 381 382 Args: 383 content (ToolResponse): The tool response content for the message. 384 """ 385 386 def __init__(self, content: ToolResponse): 387 if not isinstance(content, ToolResponse): 388 raise TypeError( 389 f"A {self.__class__.__name__} needs a ToolResponse but got {type(content)}. Check the invoke function of the OutputLessToolCallLLM node. That is the only place to return a ToolMessage." 390 ) 391 super().__init__(content=content, role=Role.tool)
A simple class that represents a message that is a tool call answer.
Arguments:
- content (ToolResponse): The tool response content for the message.
386 def __init__(self, content: ToolResponse): 387 if not isinstance(content, ToolResponse): 388 raise TypeError( 389 f"A {self.__class__.__name__} needs a ToolResponse but got {type(content)}. Check the invoke function of the OutputLessToolCallLLM node. That is the only place to return a ToolMessage." 390 ) 391 super().__init__(content=content, role=Role.tool)
A simple class that represents a message that an LLM can read.
Arguments:
- content: The content of the message. It can take on any of the following types:
- str: A simple string message.
- List[ToolCall]: A list of tool calls.
- ToolResponse: A tool response.
- BaseModel: A custom base model object.
- Stream: A stream object with a final_message and a generator.
- role: The role of the message (assistant, user, system, tool, etc.).
- inject_prompt (bool, optional): Whether to inject prompt with context variables. Defaults to True.
9class MessageHistory(List[Message]): 10 """ 11 A basic object that represents a history of messages. The object has all the same capability as a list such as 12 `.remove()`, `.append()`, etc. 13 """ 14 15 def __str__(self): 16 return "\n".join([str(message) for message in self]) 17 18 def removed_system_messages(self) -> MessageHistory: 19 """ 20 Returns a new MessageHistory object with all SystemMessages removed. 21 """ 22 return MessageHistory([msg for msg in self if msg.role != Role.system])
A basic object that represents a history of messages. The object has all the same capability as a list such as
.remove(), .append(), etc.
13class ModelProvider(str, Enum): 14 """ 15 Enum of supported LLM model providers for RailTracks. 16 17 Attributes: 18 OPENAI: OpenAI models (e.g., GPT-3, GPT-4). 19 ANTHROPIC: Anthropic models (e.g., Claude). 20 GEMINI: Google Gemini models. 21 HUGGINGFACE: HuggingFace-hosted models. 22 AZUREAI: Azure OpenAI Service models. 23 OLLAMA: Ollama local LLMs. 24 COHERE: Cohere models. 25 """ 26 27 OPENAI = "OpenAI" 28 ANTHROPIC = "Anthropic" 29 GEMINI = "Vertex_AI" 30 HUGGINGFACE = "HuggingFace" 31 AZUREAI = "AzureAI" 32 OLLAMA = "Ollama" 33 COHERE = "cohere_chat" 34 TELUS = "Telus" 35 PORTKEY = "PortKey" 36 UNKNOWN = "Unknown"
Enum of supported LLM model providers for RailTracks.
Attributes:
- OPENAI: OpenAI models (e.g., GPT-3, GPT-4).
- ANTHROPIC: Anthropic models (e.g., Claude).
- GEMINI: Google Gemini models.
- HUGGINGFACE: HuggingFace-hosted models.
- AZUREAI: Azure OpenAI Service models.
- OLLAMA: Ollama local LLMs.
- COHERE: Cohere models.
29class Tool: 30 """ 31 A quasi-immutable class designed to represent a single Tool object. 32 You pass in key details (name, description, and required parameters). 33 """ 34 35 def __init__( 36 self, 37 name: str, 38 detail: str, 39 parameters: Iterable[Parameter] | Dict[str, Any] | None = None, 40 ): 41 """ 42 Creates a new Tool instance. 43 44 Args: 45 name: The name of the tool. 46 detail: A detailed description of the tool. 47 parameters: Parameters attached to this tool; a set of Parameter objects, or a dict. 48 """ 49 50 if ( 51 isinstance(parameters, dict) and len(parameters) > 0 52 ): # if parameters is a JSON-output_schema, convert into Parameter objects (Checks should be done in validate_tool_params) 53 props = parameters.get("properties") 54 required_fields = list(parameters.get("required", [])) 55 param_objs: List[Parameter] = [] 56 for name, prop in props.items(): 57 param_objs.append( 58 parse_json_schema_to_parameter(name, prop, name in required_fields) 59 ) 60 parameters = param_objs 61 62 self._name = name 63 self._detail = detail 64 self._parameters = parameters 65 66 @property 67 def name(self) -> str: 68 """Get the name of the tool.""" 69 return self._name 70 71 @property 72 def detail(self) -> str: 73 """Returns the detailed description for this tool.""" 74 return self._detail 75 76 @property 77 def parameters(self) -> List[Parameter] | None: 78 """Gets the parameters attached to this tool (if any).""" 79 return self._parameters 80 81 def __str__(self) -> str: 82 """String representation of the tool.""" 83 if self._parameters: 84 params_str = "{" + ", ".join(str(p) for p in self._parameters) + "}" 85 return f"Tool(name={self._name}, detail={self._detail}, parameters={params_str if self._parameters else 'None'})" 86 87 @classmethod 88 def from_function( 89 cls, 90 func: Callable, 91 /, 92 *, 93 name: str | None = None, 94 details: str | None = None, 95 params: Type[BaseModel] | Dict[str, Any] | List[Parameter] | None = None, 96 ) -> Self: 97 """ 98 Creates a Tool from a Python callable. 99 Uses the function's docstring and type annotations to extract details and parameter info. 100 101 KEY NOTE: No checking is done to ensure that the inserted params match the function signature 102 103 Args: 104 func: The function to create a tool from. 105 name: Optional name for the tool. If not provided, uses the function's name. 106 details: Optional detailed description for the tool. If not provided, extracts from the function's docstring. 107 params: Optional parameters for the tool. If not provided, infers from the function's signature and docstring. 108 109 Returns: 110 A Tool instance representing the function. 111 """ 112 # TODO: add set verification to ensure that the params match the function signature 113 # Check if the function is a method in a class 114 in_class = bool(func.__qualname__ and "." in func.__qualname__) 115 116 # Parse the docstring to get parameter descriptions 117 arg_descriptions = parse_docstring_args(func.__doc__ or "") 118 119 try: 120 # Get the function signature 121 signature = inspect.signature(func) 122 except ValueError: 123 raise ToolCreationError( 124 message="Cannot convert kwargs for builtin functions.", 125 notes=[ 126 "Please use a cutom made function.", 127 "Eg.- \ndef my_custom_function(a: int, b: str):\n pass", 128 ], 129 ) 130 131 if name is not None: 132 # TODO: add some checking here to ensure that the name is valid snake case. 133 function_name = name 134 else: 135 function_name = func.__name__ 136 137 docstring = func.__doc__.strip() if func.__doc__ else "" 138 139 if params is not None: 140 parameters = params 141 else: 142 # Check for multiple Args sections (warning) 143 # Only need to do this if we need to. 144 if docstring.count("Args:") > 1: 145 warnings.warn("Multiple 'Args:' sections found in the docstring.") 146 # Create parameter handlers 147 handlers: List[ParameterHandler] = [ 148 PydanticModelHandler(), 149 SequenceParameterHandler(), 150 UnionParameterHandler(), 151 DefaultParameterHandler(), 152 ] 153 154 parameters: List[Parameter] = [] 155 156 for param in signature.parameters.values(): 157 # Skip 'self' parameter for class methods 158 if in_class and (param.name == "self" or param.name == "cls"): 159 continue 160 161 description = arg_descriptions.get(param.name, "") 162 163 # Check if the parameter is required 164 required = param.default == inspect.Parameter.empty 165 166 handler = next(h for h in handlers if h.can_handle(param.annotation)) 167 168 param_obj = handler.create_parameter( 169 param.name, param.annotation, description, required 170 ) 171 172 parameters.append(param_obj) 173 174 if details is not None: 175 main_description = details 176 else: 177 main_description = extract_main_description(docstring) 178 179 tool_info = Tool( 180 name=function_name, 181 detail=main_description, 182 parameters=parameters, 183 ) 184 return tool_info 185 186 @classmethod 187 def from_mcp(cls, tool) -> Self: 188 """ 189 Creates a Tool from an MCP tool object. 190 191 Args: 192 tool: The MCP tool to create a Tool from. 193 194 Returns: 195 A Tool instance representing the MCP tool. 196 """ 197 input_schema = getattr(tool, "inputSchema", None) 198 if not input_schema or input_schema["type"] != "object": 199 raise ToolCreationError( 200 message="The inputSchema for an MCP Tool must be 'object'. ", 201 notes=[ 202 "If an MCP tool has a different output_schema, create a GitHub issue and support will be added." 203 ], 204 ) 205 206 properties = input_schema.get("properties", {}) 207 required_fields = set(input_schema.get("required", [])) 208 param_objs = set() 209 for name, prop in properties.items(): 210 required = name in required_fields 211 param_objs.add(parse_json_schema_to_parameter(name, prop, required)) 212 213 return cls(name=tool.name, detail=tool.description, parameters=param_objs)
A quasi-immutable class designed to represent a single Tool object. You pass in key details (name, description, and required parameters).
35 def __init__( 36 self, 37 name: str, 38 detail: str, 39 parameters: Iterable[Parameter] | Dict[str, Any] | None = None, 40 ): 41 """ 42 Creates a new Tool instance. 43 44 Args: 45 name: The name of the tool. 46 detail: A detailed description of the tool. 47 parameters: Parameters attached to this tool; a set of Parameter objects, or a dict. 48 """ 49 50 if ( 51 isinstance(parameters, dict) and len(parameters) > 0 52 ): # if parameters is a JSON-output_schema, convert into Parameter objects (Checks should be done in validate_tool_params) 53 props = parameters.get("properties") 54 required_fields = list(parameters.get("required", [])) 55 param_objs: List[Parameter] = [] 56 for name, prop in props.items(): 57 param_objs.append( 58 parse_json_schema_to_parameter(name, prop, name in required_fields) 59 ) 60 parameters = param_objs 61 62 self._name = name 63 self._detail = detail 64 self._parameters = parameters
Creates a new Tool instance.
Arguments:
- name: The name of the tool.
- detail: A detailed description of the tool.
- parameters: Parameters attached to this tool; a set of Parameter objects, or a dict.
71 @property 72 def detail(self) -> str: 73 """Returns the detailed description for this tool.""" 74 return self._detail
Returns the detailed description for this tool.
76 @property 77 def parameters(self) -> List[Parameter] | None: 78 """Gets the parameters attached to this tool (if any).""" 79 return self._parameters
Gets the parameters attached to this tool (if any).
87 @classmethod 88 def from_function( 89 cls, 90 func: Callable, 91 /, 92 *, 93 name: str | None = None, 94 details: str | None = None, 95 params: Type[BaseModel] | Dict[str, Any] | List[Parameter] | None = None, 96 ) -> Self: 97 """ 98 Creates a Tool from a Python callable. 99 Uses the function's docstring and type annotations to extract details and parameter info. 100 101 KEY NOTE: No checking is done to ensure that the inserted params match the function signature 102 103 Args: 104 func: The function to create a tool from. 105 name: Optional name for the tool. If not provided, uses the function's name. 106 details: Optional detailed description for the tool. If not provided, extracts from the function's docstring. 107 params: Optional parameters for the tool. If not provided, infers from the function's signature and docstring. 108 109 Returns: 110 A Tool instance representing the function. 111 """ 112 # TODO: add set verification to ensure that the params match the function signature 113 # Check if the function is a method in a class 114 in_class = bool(func.__qualname__ and "." in func.__qualname__) 115 116 # Parse the docstring to get parameter descriptions 117 arg_descriptions = parse_docstring_args(func.__doc__ or "") 118 119 try: 120 # Get the function signature 121 signature = inspect.signature(func) 122 except ValueError: 123 raise ToolCreationError( 124 message="Cannot convert kwargs for builtin functions.", 125 notes=[ 126 "Please use a cutom made function.", 127 "Eg.- \ndef my_custom_function(a: int, b: str):\n pass", 128 ], 129 ) 130 131 if name is not None: 132 # TODO: add some checking here to ensure that the name is valid snake case. 133 function_name = name 134 else: 135 function_name = func.__name__ 136 137 docstring = func.__doc__.strip() if func.__doc__ else "" 138 139 if params is not None: 140 parameters = params 141 else: 142 # Check for multiple Args sections (warning) 143 # Only need to do this if we need to. 144 if docstring.count("Args:") > 1: 145 warnings.warn("Multiple 'Args:' sections found in the docstring.") 146 # Create parameter handlers 147 handlers: List[ParameterHandler] = [ 148 PydanticModelHandler(), 149 SequenceParameterHandler(), 150 UnionParameterHandler(), 151 DefaultParameterHandler(), 152 ] 153 154 parameters: List[Parameter] = [] 155 156 for param in signature.parameters.values(): 157 # Skip 'self' parameter for class methods 158 if in_class and (param.name == "self" or param.name == "cls"): 159 continue 160 161 description = arg_descriptions.get(param.name, "") 162 163 # Check if the parameter is required 164 required = param.default == inspect.Parameter.empty 165 166 handler = next(h for h in handlers if h.can_handle(param.annotation)) 167 168 param_obj = handler.create_parameter( 169 param.name, param.annotation, description, required 170 ) 171 172 parameters.append(param_obj) 173 174 if details is not None: 175 main_description = details 176 else: 177 main_description = extract_main_description(docstring) 178 179 tool_info = Tool( 180 name=function_name, 181 detail=main_description, 182 parameters=parameters, 183 ) 184 return tool_info
Creates a Tool from a Python callable. Uses the function's docstring and type annotations to extract details and parameter info.
KEY NOTE: No checking is done to ensure that the inserted params match the function signature
Arguments:
- func: The function to create a tool from.
- name: Optional name for the tool. If not provided, uses the function's name.
- details: Optional detailed description for the tool. If not provided, extracts from the function's docstring.
- params: Optional parameters for the tool. If not provided, infers from the function's signature and docstring.
Returns:
A Tool instance representing the function.
186 @classmethod 187 def from_mcp(cls, tool) -> Self: 188 """ 189 Creates a Tool from an MCP tool object. 190 191 Args: 192 tool: The MCP tool to create a Tool from. 193 194 Returns: 195 A Tool instance representing the MCP tool. 196 """ 197 input_schema = getattr(tool, "inputSchema", None) 198 if not input_schema or input_schema["type"] != "object": 199 raise ToolCreationError( 200 message="The inputSchema for an MCP Tool must be 'object'. ", 201 notes=[ 202 "If an MCP tool has a different output_schema, create a GitHub issue and support will be added." 203 ], 204 ) 205 206 properties = input_schema.get("properties", {}) 207 required_fields = set(input_schema.get("required", [])) 208 param_objs = set() 209 for name, prop in properties.items(): 210 required = name in required_fields 211 param_objs.add(parse_json_schema_to_parameter(name, prop, required)) 212 213 return cls(name=tool.name, detail=tool.description, parameters=param_objs)
Creates a Tool from an MCP tool object.
Arguments:
- tool: The MCP tool to create a Tool from.
Returns:
A Tool instance representing the MCP tool.
10class AnthropicLLM(ProviderLLMWrapper[_TStream], Generic[_TStream]): 11 @classmethod 12 def model_gateway(cls) -> ModelProvider: 13 return ModelProvider.ANTHROPIC
A large base class that wraps around a litellm model.
Note that the model object should be interacted with via the methods provided in the wrapper class:
chatstructuredstream_chatchat_with_tools
Each individual API should implement the required abstract_methods in order to allow users to interact with a
model of that type.
25class AzureAILLM(LiteLLMWrapper[_TStream]): 26 """Azure Foundry LLM wrapper. 27 28 Accepts either litellm prefix: 29 - ``azure/<deployment>`` — Azure OpenAI Service route; the string after the 30 slash is the user-chosen deployment name and can be anything. 31 - ``azure_ai/<model>`` — Azure AI Foundry model-inference route; the string 32 after the slash is a model identifier from Foundry's catalog. 33 34 The model string is forwarded verbatim to litellm — no client-side validation 35 is done against a static catalog, since deployment names are user-defined and 36 can't be known ahead of time. 37 """ 38 39 @classmethod 40 def model_gateway(cls): 41 return ModelProvider.AZUREAI 42 43 def model_provider(self) -> ModelProvider: 44 return self.model_gateway() 45 46 def __init__( 47 self, 48 model_name: str, 49 *, 50 temperature: float | None = None, 51 top_p: float | None = None, 52 max_tokens: int | None = None, 53 frequency_penalty: float | None = None, 54 presence_penalty: float | None = None, 55 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 56 service_tier: str | None = None, 57 verbosity: Literal["low", "medium", "high"] | None = None, 58 retry_approach: RetryApproach | None = None, 59 **kwargs, 60 ): 61 """Initialize an Azure AI LLM instance. 62 63 Args: 64 model_name (str): Full litellm model string, e.g. ``azure/my-deployment`` 65 or ``azure_ai/deepseek-r1``. See the class docstring for the 66 difference between the two prefixes. 67 temperature (float | None, optional): Sampling temperature for generation (e.g. 0.0–2.0). 68 If None, the provider default is used. 69 top_p (float | None, optional): Nucleus sampling threshold. 70 max_tokens (int | None, optional): Maximum tokens to generate. 71 frequency_penalty (float | None, optional): Penalizes tokens by how often 72 they've already appeared. 73 presence_penalty (float | None, optional): Penalizes tokens that have 74 already appeared at all. 75 reasoning_effort (Literal["minimal", "low", "medium", "high"] | None, optional): 76 Requested reasoning effort for reasoning-capable models. 77 service_tier (str | None, optional): Requested service tier. Provider-specific. 78 verbosity (Literal["low", "medium", "high"] | None, optional): Requested 79 output verbosity for models that support it. 80 retry_approach (RetryApproach | None, optional): Retry strategy for transient 81 failures. 82 **kwargs: Additional arguments passed to the parent LiteLLMWrapper. 83 84 Raises: 85 AzureAIError: If the specified model is not available or if there are issues with the Azure AI service. 86 """ 87 if kwargs.get("stream"): 88 warn_pending_change( 89 "Constructing a model with `stream=True`", 90 change="is removed", 91 instead="rt.astream(agent, ...) to stream an agent run", 92 detail=( 93 "Streaming becomes async in 1.5.0: per-call model methods " 94 "(astream_chat, astream_chat_with_tools, astream_structured) " 95 "replace the streamed return value of chat()." 96 ), 97 ) 98 99 super().__init__( 100 model_name, 101 temperature=temperature, 102 top_p=top_p, 103 max_tokens=max_tokens, 104 frequency_penalty=frequency_penalty, 105 presence_penalty=presence_penalty, 106 reasoning_effort=reasoning_effort, 107 service_tier=service_tier, 108 verbosity=verbosity, 109 retry_approach=retry_approach, 110 **kwargs, 111 ) 112 self.logger = logger 113 114 def chat(self, messages: MessageHistory): 115 try: 116 return super().chat(messages) 117 except InternalServerError as e: 118 raise AzureAIError( 119 reason=f"Azure AI LLM error while processing the request: {e}" 120 ) from e 121 122 def chat_with_tools(self, messages: MessageHistory, tools: List[Tool]): 123 try: 124 return super().chat_with_tools(messages, tools) 125 except InternalServerError as e: 126 raise AzureAIError( 127 reason=f"Azure AI LLM error while processing the request: {e}" 128 ) from e
Azure Foundry LLM wrapper.
Accepts either litellm prefix:
azure/<deployment>— Azure OpenAI Service route; the string after the slash is the user-chosen deployment name and can be anything.azure_ai/<model>— Azure AI Foundry model-inference route; the string after the slash is a model identifier from Foundry's catalog.
The model string is forwarded verbatim to litellm — no client-side validation is done against a static catalog, since deployment names are user-defined and can't be known ahead of time.
46 def __init__( 47 self, 48 model_name: str, 49 *, 50 temperature: float | None = None, 51 top_p: float | None = None, 52 max_tokens: int | None = None, 53 frequency_penalty: float | None = None, 54 presence_penalty: float | None = None, 55 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 56 service_tier: str | None = None, 57 verbosity: Literal["low", "medium", "high"] | None = None, 58 retry_approach: RetryApproach | None = None, 59 **kwargs, 60 ): 61 """Initialize an Azure AI LLM instance. 62 63 Args: 64 model_name (str): Full litellm model string, e.g. ``azure/my-deployment`` 65 or ``azure_ai/deepseek-r1``. See the class docstring for the 66 difference between the two prefixes. 67 temperature (float | None, optional): Sampling temperature for generation (e.g. 0.0–2.0). 68 If None, the provider default is used. 69 top_p (float | None, optional): Nucleus sampling threshold. 70 max_tokens (int | None, optional): Maximum tokens to generate. 71 frequency_penalty (float | None, optional): Penalizes tokens by how often 72 they've already appeared. 73 presence_penalty (float | None, optional): Penalizes tokens that have 74 already appeared at all. 75 reasoning_effort (Literal["minimal", "low", "medium", "high"] | None, optional): 76 Requested reasoning effort for reasoning-capable models. 77 service_tier (str | None, optional): Requested service tier. Provider-specific. 78 verbosity (Literal["low", "medium", "high"] | None, optional): Requested 79 output verbosity for models that support it. 80 retry_approach (RetryApproach | None, optional): Retry strategy for transient 81 failures. 82 **kwargs: Additional arguments passed to the parent LiteLLMWrapper. 83 84 Raises: 85 AzureAIError: If the specified model is not available or if there are issues with the Azure AI service. 86 """ 87 if kwargs.get("stream"): 88 warn_pending_change( 89 "Constructing a model with `stream=True`", 90 change="is removed", 91 instead="rt.astream(agent, ...) to stream an agent run", 92 detail=( 93 "Streaming becomes async in 1.5.0: per-call model methods " 94 "(astream_chat, astream_chat_with_tools, astream_structured) " 95 "replace the streamed return value of chat()." 96 ), 97 ) 98 99 super().__init__( 100 model_name, 101 temperature=temperature, 102 top_p=top_p, 103 max_tokens=max_tokens, 104 frequency_penalty=frequency_penalty, 105 presence_penalty=presence_penalty, 106 reasoning_effort=reasoning_effort, 107 service_tier=service_tier, 108 verbosity=verbosity, 109 retry_approach=retry_approach, 110 **kwargs, 111 ) 112 self.logger = logger
Initialize an Azure AI LLM instance.
Arguments:
- model_name (str): Full litellm model string, e.g.
azure/my-deploymentorazure_ai/deepseek-r1. See the class docstring for the difference between the two prefixes. - temperature (float | None, optional): Sampling temperature for generation (e.g. 0.0–2.0). If None, the provider default is used.
- top_p (float | None, optional): Nucleus sampling threshold.
- max_tokens (int | None, optional): Maximum tokens to generate.
- frequency_penalty (float | None, optional): Penalizes tokens by how often they've already appeared.
- presence_penalty (float | None, optional): Penalizes tokens that have already appeared at all.
- reasoning_effort (Literal["minimal", "low", "medium", "high"] | None, optional): Requested reasoning effort for reasoning-capable models.
- service_tier (str | None, optional): Requested service tier. Provider-specific.
- verbosity (Literal["low", "medium", "high"] | None, optional): Requested output verbosity for models that support it.
- retry_approach (RetryApproach | None, optional): Retry strategy for transient failures.
- **kwargs: Additional arguments passed to the parent LiteLLMWrapper.
Raises:
- AzureAIError: If the specified model is not available or if there are issues with the Azure AI service.
Gets the API distrubutor of the model. Note nessecarily the same as the model itself.
E.g. if you are calling openai LLM through Azure AI foundry
114 def chat(self, messages: MessageHistory): 115 try: 116 return super().chat(messages) 117 except InternalServerError as e: 118 raise AzureAIError( 119 reason=f"Azure AI LLM error while processing the request: {e}" 120 ) from e
Chat with the model using the provided messages.
122 def chat_with_tools(self, messages: MessageHistory, tools: List[Tool]): 123 try: 124 return super().chat_with_tools(messages, tools) 125 except InternalServerError as e: 126 raise AzureAIError( 127 reason=f"Azure AI LLM error while processing the request: {e}" 128 ) from e
Chat with the model using the provided messages and tools.
6class CohereLLM(ProviderLLMWrapper): 7 """ 8 A wrapper that provides access to the Cohere API. 9 """ 10 11 @classmethod 12 def model_gateway(cls): 13 return ModelProvider.COHERE
A wrapper that provides access to the Cohere API.
11class HuggingFaceLLM(ProviderLLMWrapper[_TStream]): 12 def _pre_init_provider_check(self, model_name): 13 """called by __init__ before the super call in ProviderLLMWrapper""" 14 # for huggingface models there is no good way of using `get_llm_provider` to check if the model is valid. 15 # so we are just goinog to add `huggingface/` to the model name in case it is not there. 16 # if the model name happens to be invalid, the error will be generated at runtime during `litellm.completion`. See `_litellm_wrapper.py` 17 if model_name.startswith(self.model_provider().lower()): 18 model_name = "/".join(model_name.split("/")[1:]) 19 try: 20 assert len(model_name.split("/")) == 3, "Invalid model name" 21 except AssertionError as e: 22 raise ModelNotFoundError( 23 reason=e.args[0], 24 notes=[ 25 "Model name must be of the format `huggingface/<provider>/<hf_org_or_user>/<hf_model>` or `<provider>/<hf_org_or_user>/<hf_model>`", 26 "We only support the huggingface Serverless Inference Provider Models.", 27 "Provider List: https://docs.litellm.ai/docs/providers", 28 ], 29 ) 30 return model_name 31 32 def model_provider(self) -> ModelProvider: 33 # TODO implement logic for all the possible providers attached the hugging face. 34 return ModelProvider.HUGGINGFACE 35 36 def _validate_tool_calling_support(self): 37 # NOTE: special exception case for huggingface 38 # Due to the wide range of huggingface models, `litellm.supports_function_calling` isn't always accurate. 39 # so we are just going to skip the check and the error (if any) will be generated at runtime during `litellm.completion`. 40 pass 41 42 @classmethod 43 def model_gateway(cls): 44 return ModelProvider.HUGGINGFACE
A large base class that wraps around a litellm model.
Note that the model object should be interacted with via the methods provided in the wrapper class:
chatstructuredstream_chatchat_with_tools
Each individual API should implement the required abstract_methods in order to allow users to interact with a
model of that type.
10class OpenAILLM(ProviderLLMWrapper[_TStream], Generic[_TStream]): 11 """ 12 A wrapper that provides access to the OPENAI API. 13 """ 14 15 @classmethod 16 def model_gateway(cls): 17 return ModelProvider.OPENAI
A wrapper that provides access to the OPENAI API.
10class GeminiLLM(ProviderLLMWrapper[_TStream]): 11 def full_model_name(self, model_name: str) -> str: 12 # for gemini models through litellm, we need 'gemini/{model_name}' format, but we do this after the checks in ProiLLMWrapper init 13 return f"gemini/{model_name}" 14 15 @classmethod 16 def model_gateway(cls): 17 return ModelProvider.GEMINI # litellm uses this for the provider for Gemini, we are using this in the checks in _provider_wrapper.py
A large base class that wraps around a litellm model.
Note that the model object should be interacted with via the methods provided in the wrapper class:
chatstructuredstream_chatchat_with_tools
Each individual API should implement the required abstract_methods in order to allow users to interact with a
model of that type.
11 def full_model_name(self, model_name: str) -> str: 12 # for gemini models through litellm, we need 'gemini/{model_name}' format, but we do this after the checks in ProiLLMWrapper init 13 return f"gemini/{model_name}"
After the provider is checked, this method is called to get the full model name
15 @classmethod 16 def model_gateway(cls): 17 return ModelProvider.GEMINI # litellm uses this for the provider for Gemini, we are using this in the checks in _provider_wrapper.py
Gets the API distrubutor of the model. Note nessecarily the same as the model itself.
E.g. if you are calling openai LLM through Azure AI foundry
28class OllamaLLM(LiteLLMWrapper[_TStream]): 29 def __init__( 30 self, 31 model_name: str, 32 stream: _TStream = False, 33 domain: Literal["default", "auto", "custom"] = "default", 34 custom_domain: str | None = None, 35 temperature: float | None = None, 36 top_p: float | None = None, 37 max_tokens: int | None = None, 38 frequency_penalty: float | None = None, 39 presence_penalty: float | None = None, 40 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 41 service_tier: str | None = None, 42 verbosity: Literal["low", "medium", "high"] | None = None, 43 retry_approach: RetryApproach | None = None, 44 **kwargs, 45 ): 46 """Initialize an Ollama LLM instance. 47 48 Args: 49 model_name (str): Name of the Ollama model to use. 50 stream (bool): Whether to stream the response. 51 domain (Literal["default", "auto", "custom"], optional): The domain configuration mode. 52 - "default": Uses the default localhost domain (http://localhost:11434) 53 - "auto": Uses the OLLAMA_HOST environment variable, raises OllamaError if not set 54 - "custom": Uses the provided custom_domain parameter, raises OllamaError if not provided 55 Defaults to "default". 56 custom_domain (str | None, optional): Custom domain URL to use when domain is set to "custom". 57 Must be provided if domain="custom". Defaults to None. 58 temperature (float | None, optional): Sampling temperature for generation (e.g. 0.0–2.0). 59 If None, the provider default is used. 60 top_p (float | None, optional): Nucleus sampling threshold. 61 max_tokens (int | None, optional): Maximum tokens to generate. 62 frequency_penalty (float | None, optional): Penalizes tokens by how often 63 they've already appeared. 64 presence_penalty (float | None, optional): Penalizes tokens that have 65 already appeared at all. 66 reasoning_effort (Literal["minimal", "low", "medium", "high"] | None, optional): 67 Requested reasoning effort for reasoning-capable models. 68 service_tier (str | None, optional): Requested service tier. Provider-specific. 69 verbosity (Literal["low", "medium", "high"] | None, optional): Requested 70 output verbosity for models that support it. 71 retry_approach (RetryApproach | None, optional): Retry strategy for transient 72 failures. 73 **kwargs: Additional arguments passed to the parent LiteLLMWrapper. 74 75 Raises: 76 OllamaError: If: 77 - domain is "auto" and OLLAMA_HOST environment variable is not set 78 - domain is "custom" and custom_domain is not provided 79 - specified model is not available on the server 80 RequestException: If connection to Ollama server fails 81 """ 82 if stream: 83 warn_pending_change( 84 "Constructing a model with `stream=True`", 85 change="is removed", 86 instead="rt.astream(agent, ...) to stream an agent run", 87 detail=( 88 "Streaming becomes async in 1.5.0: per-call model methods " 89 "(astream_chat, astream_chat_with_tools, astream_structured) " 90 "replace the streamed return value of chat()." 91 ), 92 ) 93 94 if not model_name.startswith("ollama/"): 95 logger.warning( 96 f"Prepending 'ollama/' to model name '{model_name}' for Ollama" 97 ) 98 model_name = f"ollama/{model_name}" 99 super().__init__( 100 model_name=model_name, 101 stream=stream, 102 temperature=temperature, 103 top_p=top_p, 104 max_tokens=max_tokens, 105 frequency_penalty=frequency_penalty, 106 presence_penalty=presence_penalty, 107 reasoning_effort=reasoning_effort, 108 service_tier=service_tier, 109 verbosity=verbosity, 110 retry_approach=retry_approach, 111 **kwargs, 112 ) 113 114 match domain: 115 case "default": 116 self.domain = DEFAULT_DOMAIN 117 case "auto": 118 domain_from_env = os.getenv("OLLAMA_HOST") 119 if domain_from_env is None: 120 raise OllamaError("OLLAMA_HOST environment variable not set") 121 self.domain = domain_from_env 122 case "custom": 123 if custom_domain is None: 124 raise OllamaError( 125 "Custom domain must be provided when domain is set to 'custom'" 126 ) 127 self.domain = custom_domain 128 129 self._run_check( 130 "api/tags" 131 ) # This will crash the workflow if Ollama is not setup properly 132 133 def _run_check(self, endpoint: str): 134 url = f"{self.domain}/{endpoint.lstrip('/')}" 135 try: 136 response = requests.get(url) 137 response.raise_for_status() 138 139 models = response.json() 140 141 model_names = {model["name"] for model in models["models"]} 142 143 model_name = self.model_name().rsplit("/", 1)[ 144 -1 145 ] # extract the model name if the provider is also included 146 147 if model_name not in model_names: 148 error_msg = f"{self.model_name()} not available on server {self.domain}. Avaiable models are: {model_names}" 149 logger.error(error_msg) 150 raise OllamaError(error_msg) 151 152 except OllamaError as e: 153 logger.error(e) 154 raise 155 156 except requests.exceptions.RequestException as e: 157 logger.error(e) 158 raise 159 160 def chat_with_tools(self, messages, tools): 161 if not supports_function_calling(model=self._model_name): 162 raise FunctionCallingNotSupportedError(self._model_name) 163 164 return super().chat_with_tools(messages, tools) 165 166 @classmethod 167 def model_gateway(cls): 168 return ModelProvider.OLLAMA 169 170 def model_provider(self) -> ModelProvider: 171 """Returns the name of the provider""" 172 return self.model_gateway()
A large base class that wraps around a litellm model.
Note that the model object should be interacted with via the methods provided in the wrapper class:
chatstructuredstream_chatchat_with_tools
Each individual API should implement the required abstract_methods in order to allow users to interact with a
model of that type.
29 def __init__( 30 self, 31 model_name: str, 32 stream: _TStream = False, 33 domain: Literal["default", "auto", "custom"] = "default", 34 custom_domain: str | None = None, 35 temperature: float | None = None, 36 top_p: float | None = None, 37 max_tokens: int | None = None, 38 frequency_penalty: float | None = None, 39 presence_penalty: float | None = None, 40 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 41 service_tier: str | None = None, 42 verbosity: Literal["low", "medium", "high"] | None = None, 43 retry_approach: RetryApproach | None = None, 44 **kwargs, 45 ): 46 """Initialize an Ollama LLM instance. 47 48 Args: 49 model_name (str): Name of the Ollama model to use. 50 stream (bool): Whether to stream the response. 51 domain (Literal["default", "auto", "custom"], optional): The domain configuration mode. 52 - "default": Uses the default localhost domain (http://localhost:11434) 53 - "auto": Uses the OLLAMA_HOST environment variable, raises OllamaError if not set 54 - "custom": Uses the provided custom_domain parameter, raises OllamaError if not provided 55 Defaults to "default". 56 custom_domain (str | None, optional): Custom domain URL to use when domain is set to "custom". 57 Must be provided if domain="custom". Defaults to None. 58 temperature (float | None, optional): Sampling temperature for generation (e.g. 0.0–2.0). 59 If None, the provider default is used. 60 top_p (float | None, optional): Nucleus sampling threshold. 61 max_tokens (int | None, optional): Maximum tokens to generate. 62 frequency_penalty (float | None, optional): Penalizes tokens by how often 63 they've already appeared. 64 presence_penalty (float | None, optional): Penalizes tokens that have 65 already appeared at all. 66 reasoning_effort (Literal["minimal", "low", "medium", "high"] | None, optional): 67 Requested reasoning effort for reasoning-capable models. 68 service_tier (str | None, optional): Requested service tier. Provider-specific. 69 verbosity (Literal["low", "medium", "high"] | None, optional): Requested 70 output verbosity for models that support it. 71 retry_approach (RetryApproach | None, optional): Retry strategy for transient 72 failures. 73 **kwargs: Additional arguments passed to the parent LiteLLMWrapper. 74 75 Raises: 76 OllamaError: If: 77 - domain is "auto" and OLLAMA_HOST environment variable is not set 78 - domain is "custom" and custom_domain is not provided 79 - specified model is not available on the server 80 RequestException: If connection to Ollama server fails 81 """ 82 if stream: 83 warn_pending_change( 84 "Constructing a model with `stream=True`", 85 change="is removed", 86 instead="rt.astream(agent, ...) to stream an agent run", 87 detail=( 88 "Streaming becomes async in 1.5.0: per-call model methods " 89 "(astream_chat, astream_chat_with_tools, astream_structured) " 90 "replace the streamed return value of chat()." 91 ), 92 ) 93 94 if not model_name.startswith("ollama/"): 95 logger.warning( 96 f"Prepending 'ollama/' to model name '{model_name}' for Ollama" 97 ) 98 model_name = f"ollama/{model_name}" 99 super().__init__( 100 model_name=model_name, 101 stream=stream, 102 temperature=temperature, 103 top_p=top_p, 104 max_tokens=max_tokens, 105 frequency_penalty=frequency_penalty, 106 presence_penalty=presence_penalty, 107 reasoning_effort=reasoning_effort, 108 service_tier=service_tier, 109 verbosity=verbosity, 110 retry_approach=retry_approach, 111 **kwargs, 112 ) 113 114 match domain: 115 case "default": 116 self.domain = DEFAULT_DOMAIN 117 case "auto": 118 domain_from_env = os.getenv("OLLAMA_HOST") 119 if domain_from_env is None: 120 raise OllamaError("OLLAMA_HOST environment variable not set") 121 self.domain = domain_from_env 122 case "custom": 123 if custom_domain is None: 124 raise OllamaError( 125 "Custom domain must be provided when domain is set to 'custom'" 126 ) 127 self.domain = custom_domain 128 129 self._run_check( 130 "api/tags" 131 ) # This will crash the workflow if Ollama is not setup properly
Initialize an Ollama LLM instance.
Arguments:
- model_name (str): Name of the Ollama model to use.
- stream (bool): Whether to stream the response.
- domain (Literal["default", "auto", "custom"], optional): The domain configuration mode.
- "default": Uses the default localhost domain (http://localhost:11434)
- "auto": Uses the OLLAMA_HOST environment variable, raises OllamaError if not set
- "custom": Uses the provided custom_domain parameter, raises OllamaError if not provided Defaults to "default".
- custom_domain (str | None, optional): Custom domain URL to use when domain is set to "custom". Must be provided if domain="custom". Defaults to None.
- temperature (float | None, optional): Sampling temperature for generation (e.g. 0.0–2.0). If None, the provider default is used.
- top_p (float | None, optional): Nucleus sampling threshold.
- max_tokens (int | None, optional): Maximum tokens to generate.
- frequency_penalty (float | None, optional): Penalizes tokens by how often they've already appeared.
- presence_penalty (float | None, optional): Penalizes tokens that have already appeared at all.
- reasoning_effort (Literal["minimal", "low", "medium", "high"] | None, optional): Requested reasoning effort for reasoning-capable models.
- service_tier (str | None, optional): Requested service tier. Provider-specific.
- verbosity (Literal["low", "medium", "high"] | None, optional): Requested output verbosity for models that support it.
- retry_approach (RetryApproach | None, optional): Retry strategy for transient failures.
- **kwargs: Additional arguments passed to the parent LiteLLMWrapper.
Raises:
- OllamaError: If:
- domain is "auto" and OLLAMA_HOST environment variable is not set
- domain is "custom" and custom_domain is not provided
- specified model is not available on the server
- RequestException: If connection to Ollama server fails
160 def chat_with_tools(self, messages, tools): 161 if not supports_function_calling(model=self._model_name): 162 raise FunctionCallingNotSupportedError(self._model_name) 163 164 return super().chat_with_tools(messages, tools)
Chat with the model using the provided messages and tools.
Gets the API distrubutor of the model. Note nessecarily the same as the model itself.
E.g. if you are calling openai LLM through Azure AI foundry
14class PortKeyLLM(OpenAICompatibleProvider[_TStream]): 15 def __init__( 16 self, 17 model_name: str, 18 *, 19 stream: _TStream = False, 20 api_key: str | None = None, 21 temperature: float | None = None, 22 top_p: float | None = None, 23 max_tokens: int | None = None, 24 frequency_penalty: float | None = None, 25 presence_penalty: float | None = None, 26 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 27 service_tier: str | None = None, 28 verbosity: Literal["low", "medium", "high"] | None = None, 29 retry_approach: RetryApproach | None = None, 30 **kwargs: Any, 31 ): 32 try: 33 from portkey_ai import Portkey 34 except ImportError: 35 raise ImportError( 36 "Could not import portkey_ai package. Use railtracks[portkey]" 37 ) 38 39 if api_key is None: 40 try: 41 api_key = os.environ["PORTKEY_API_KEY"] 42 except KeyError: 43 raise KeyError("Please set your PORTKEY_API_KEY in your .env file.") 44 45 portkey = Portkey(api_key=api_key) 46 47 super().__init__( 48 model_name, 49 stream=stream, 50 api_base=portkey.base_url, 51 api_key=portkey.api_key, 52 temperature=temperature, 53 top_p=top_p, 54 max_tokens=max_tokens, 55 frequency_penalty=frequency_penalty, 56 presence_penalty=presence_penalty, 57 reasoning_effort=reasoning_effort, 58 service_tier=service_tier, 59 verbosity=verbosity, 60 retry_approach=retry_approach, 61 **kwargs, 62 ) 63 64 @classmethod 65 def model_gateway(cls): 66 return ModelProvider.PORTKEY 67 68 def model_provider(self): 69 # TODO: Implement specialized logic to determine the model provider 70 return ModelProvider.PORTKEY
A large base class that wraps around a litellm model.
Note that the model object should be interacted with via the methods provided in the wrapper class:
chatstructuredstream_chatchat_with_tools
Each individual API should implement the required abstract_methods in order to allow users to interact with a
model of that type.
15 def __init__( 16 self, 17 model_name: str, 18 *, 19 stream: _TStream = False, 20 api_key: str | None = None, 21 temperature: float | None = None, 22 top_p: float | None = None, 23 max_tokens: int | None = None, 24 frequency_penalty: float | None = None, 25 presence_penalty: float | None = None, 26 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 27 service_tier: str | None = None, 28 verbosity: Literal["low", "medium", "high"] | None = None, 29 retry_approach: RetryApproach | None = None, 30 **kwargs: Any, 31 ): 32 try: 33 from portkey_ai import Portkey 34 except ImportError: 35 raise ImportError( 36 "Could not import portkey_ai package. Use railtracks[portkey]" 37 ) 38 39 if api_key is None: 40 try: 41 api_key = os.environ["PORTKEY_API_KEY"] 42 except KeyError: 43 raise KeyError("Please set your PORTKEY_API_KEY in your .env file.") 44 45 portkey = Portkey(api_key=api_key) 46 47 super().__init__( 48 model_name, 49 stream=stream, 50 api_base=portkey.base_url, 51 api_key=portkey.api_key, 52 temperature=temperature, 53 top_p=top_p, 54 max_tokens=max_tokens, 55 frequency_penalty=frequency_penalty, 56 presence_penalty=presence_penalty, 57 reasoning_effort=reasoning_effort, 58 service_tier=service_tier, 59 verbosity=verbosity, 60 retry_approach=retry_approach, 61 **kwargs, 62 )
Initialize an OpenAI-compatible gateway LLM instance (e.g. via PortKey).
See ProviderLLMWrapper.__init__ for the full per-hyperparameter description
of the common hyperparameters below (top_p, max_tokens, frequency_penalty,
presence_penalty, reasoning_effort, service_tier, verbosity).
Note:
Gateway-style providers can't be reliably introspected by litellm, so neither per-model hyperparameter support nor mutual-exclusion checks run here (see
_validate_common_hyperparameter_supportoverride below) — every hyperparameter, valid or not, is passed straight through and any error surfaces from the gateway or upstream provider directly.
12class OpenAICompatibleProvider(ProviderLLMWrapper[_TStream], ABC): 13 def __init__( 14 self, 15 model_name: str, 16 *, 17 stream: _TStream = False, 18 api_base: str, 19 api_key: str, 20 temperature: float | None = None, 21 top_p: float | None = None, 22 max_tokens: int | None = None, 23 frequency_penalty: float | None = None, 24 presence_penalty: float | None = None, 25 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 26 service_tier: str | None = None, 27 verbosity: Literal["low", "medium", "high"] | None = None, 28 retry_approach: RetryApproach | None = None, 29 **kwargs: Any, 30 ): 31 """Initialize an OpenAI-compatible gateway LLM instance (e.g. via PortKey). 32 33 See `ProviderLLMWrapper.__init__` for the full per-hyperparameter description 34 of the common hyperparameters below (`top_p`, `max_tokens`, `frequency_penalty`, 35 `presence_penalty`, `reasoning_effort`, `service_tier`, `verbosity`). 36 37 Note: 38 Gateway-style providers can't be reliably introspected by litellm, so 39 neither per-model hyperparameter support nor mutual-exclusion checks run 40 here (see `_validate_common_hyperparameter_support` override below) — 41 every hyperparameter, valid or not, is passed straight through and any 42 error surfaces from the gateway or upstream provider directly. 43 """ 44 super().__init__( 45 model_name, 46 stream=stream, 47 api_base=api_base, 48 api_key=api_key, 49 temperature=temperature, 50 top_p=top_p, 51 max_tokens=max_tokens, 52 frequency_penalty=frequency_penalty, 53 presence_penalty=presence_penalty, 54 reasoning_effort=reasoning_effort, 55 service_tier=service_tier, 56 verbosity=verbosity, 57 retry_approach=retry_approach, 58 **kwargs, 59 ) 60 61 def full_model_name(self, model_name: str) -> str: 62 return f"openai/{model_name}" 63 64 @classmethod 65 def model_gateway(cls) -> ModelProvider: 66 return ModelProvider.UNKNOWN 67 68 def _pre_init_provider_check(self, model_name: str): 69 # For OpenAI compatible providers, we skip the provider check since there is no way to do it. 70 return model_name 71 72 def _validate_tool_calling_support(self): 73 # For OpenAI compatible providers, we skip the tool calling support check since there is no way to do it. 74 return 75 76 def _validate_common_hyperparameter_support(self) -> None: 77 # For OpenAI compatible providers, litellm can't reliably introspect 78 # gateway-style providers, so we skip the common hyperparameter support check. 79 return
A large base class that wraps around a litellm model.
Note that the model object should be interacted with via the methods provided in the wrapper class:
chatstructuredstream_chatchat_with_tools
Each individual API should implement the required abstract_methods in order to allow users to interact with a
model of that type.
13 def __init__( 14 self, 15 model_name: str, 16 *, 17 stream: _TStream = False, 18 api_base: str, 19 api_key: str, 20 temperature: float | None = None, 21 top_p: float | None = None, 22 max_tokens: int | None = None, 23 frequency_penalty: float | None = None, 24 presence_penalty: float | None = None, 25 reasoning_effort: Literal["minimal", "low", "medium", "high"] | None = None, 26 service_tier: str | None = None, 27 verbosity: Literal["low", "medium", "high"] | None = None, 28 retry_approach: RetryApproach | None = None, 29 **kwargs: Any, 30 ): 31 """Initialize an OpenAI-compatible gateway LLM instance (e.g. via PortKey). 32 33 See `ProviderLLMWrapper.__init__` for the full per-hyperparameter description 34 of the common hyperparameters below (`top_p`, `max_tokens`, `frequency_penalty`, 35 `presence_penalty`, `reasoning_effort`, `service_tier`, `verbosity`). 36 37 Note: 38 Gateway-style providers can't be reliably introspected by litellm, so 39 neither per-model hyperparameter support nor mutual-exclusion checks run 40 here (see `_validate_common_hyperparameter_support` override below) — 41 every hyperparameter, valid or not, is passed straight through and any 42 error surfaces from the gateway or upstream provider directly. 43 """ 44 super().__init__( 45 model_name, 46 stream=stream, 47 api_base=api_base, 48 api_key=api_key, 49 temperature=temperature, 50 top_p=top_p, 51 max_tokens=max_tokens, 52 frequency_penalty=frequency_penalty, 53 presence_penalty=presence_penalty, 54 reasoning_effort=reasoning_effort, 55 service_tier=service_tier, 56 verbosity=verbosity, 57 retry_approach=retry_approach, 58 **kwargs, 59 )
Initialize an OpenAI-compatible gateway LLM instance (e.g. via PortKey).
See ProviderLLMWrapper.__init__ for the full per-hyperparameter description
of the common hyperparameters below (top_p, max_tokens, frequency_penalty,
presence_penalty, reasoning_effort, service_tier, verbosity).
Note:
Gateway-style providers can't be reliably introspected by litellm, so neither per-model hyperparameter support nor mutual-exclusion checks run here (see
_validate_common_hyperparameter_supportoverride below) — every hyperparameter, valid or not, is passed straight through and any error surfaces from the gateway or upstream provider directly.
After the provider is checked, this method is called to get the full model name
53class Parameter(ABC): 54 """ 55 Abstract Base Parameter class with default simple parameter behavior. 56 """ 57 58 param_type: Optional[Union[str, List[str]]] = None # class var for default type 59 60 def __init__( 61 self, 62 name: str, 63 description: Optional[str] = None, 64 required: bool = True, 65 default: Any = None, 66 enum: Optional[List[Any]] = None, 67 default_present: bool = False, 68 param_type: ParameterTypeInput = None, 69 ): 70 """ 71 Initialize a Parameter instance. 72 73 Args: 74 name (str): Name of the parameter. 75 description (Optional[str]): Description of the parameter. 76 required (bool): Whether the parameter is required. 77 default (Any): Default value for the parameter. 78 enum (Optional[List[Any]]): Allowed values for the parameter. 79 default_present (bool): Whether a default value is explicitly set. 80 param_type: JSON schema type string (e.g. ``\"string\"``), :class:`ParameterType`, 81 a Python builtin type (e.g. ``str`` → ``\"string\"``), or a list for unions. 82 """ 83 self.name = name 84 self.description = description or "" 85 self.required = required 86 self.default = default 87 self.enum = enum 88 self.default_present = default_present 89 if param_type is not None: 90 if isinstance(param_type, list): 91 self.param_type = [ 92 _normalize_param_type_scalar(pt) for pt in param_type 93 ] 94 else: 95 self.param_type = _normalize_param_type_scalar(param_type) 96 elif hasattr(self, "param_type") and self.param_type is None: 97 self.param_type = None 98 99 def to_json_schema(self) -> Dict[str, Any]: 100 # Base dictionary with type and optional description 101 schema_dict: Dict[str, Any] = { 102 "type": self.param_type.value 103 if isinstance(self.param_type, ParameterType) 104 else self.param_type 105 } 106 if self.description: 107 schema_dict["description"] = self.description 108 109 # Handle enum 110 if self.enum: 111 schema_dict["enum"] = self.enum 112 113 # Handle default 114 # default can be None, 0, False; None means optional parameter 115 if self.default_present: 116 schema_dict["default"] = self.default 117 elif isinstance(self.param_type, list) and "none" in self.param_type: 118 schema_dict["default"] = None 119 120 return schema_dict 121 122 def __repr__(self) -> str: 123 return ( 124 f"Parameter(name={self.name!r}, param_type={self.param_type!r}, " 125 f"description={self.description!r}, required={self.required!r}, " 126 f"default={self.default!r}, enum={self.enum!r})" 127 )
Abstract Base Parameter class with default simple parameter behavior.
60 def __init__( 61 self, 62 name: str, 63 description: Optional[str] = None, 64 required: bool = True, 65 default: Any = None, 66 enum: Optional[List[Any]] = None, 67 default_present: bool = False, 68 param_type: ParameterTypeInput = None, 69 ): 70 """ 71 Initialize a Parameter instance. 72 73 Args: 74 name (str): Name of the parameter. 75 description (Optional[str]): Description of the parameter. 76 required (bool): Whether the parameter is required. 77 default (Any): Default value for the parameter. 78 enum (Optional[List[Any]]): Allowed values for the parameter. 79 default_present (bool): Whether a default value is explicitly set. 80 param_type: JSON schema type string (e.g. ``\"string\"``), :class:`ParameterType`, 81 a Python builtin type (e.g. ``str`` → ``\"string\"``), or a list for unions. 82 """ 83 self.name = name 84 self.description = description or "" 85 self.required = required 86 self.default = default 87 self.enum = enum 88 self.default_present = default_present 89 if param_type is not None: 90 if isinstance(param_type, list): 91 self.param_type = [ 92 _normalize_param_type_scalar(pt) for pt in param_type 93 ] 94 else: 95 self.param_type = _normalize_param_type_scalar(param_type) 96 elif hasattr(self, "param_type") and self.param_type is None: 97 self.param_type = None
Initialize a Parameter instance.
Arguments:
- name (str): Name of the parameter.
- description (Optional[str]): Description of the parameter.
- required (bool): Whether the parameter is required.
- default (Any): Default value for the parameter.
- enum (Optional[List[Any]]): Allowed values for the parameter.
- default_present (bool): Whether a default value is explicitly set.
- param_type: JSON schema type string (e.g.
"string"),ParameterType, a Python builtin type (e.g.str→"string"), or a list for unions.
99 def to_json_schema(self) -> Dict[str, Any]: 100 # Base dictionary with type and optional description 101 schema_dict: Dict[str, Any] = { 102 "type": self.param_type.value 103 if isinstance(self.param_type, ParameterType) 104 else self.param_type 105 } 106 if self.description: 107 schema_dict["description"] = self.description 108 109 # Handle enum 110 if self.enum: 111 schema_dict["enum"] = self.enum 112 113 # Handle default 114 # default can be None, 0, False; None means optional parameter 115 if self.default_present: 116 schema_dict["default"] = self.default 117 elif isinstance(self.param_type, list) and "none" in self.param_type: 118 schema_dict["default"] = None 119 120 return schema_dict
9class UnionParameter(Parameter): 10 """Parameter representing a union type.""" 11 12 param_type: List[str] 13 14 def __init__( 15 self, 16 name: str, 17 options: List[Parameter], 18 description: Optional[str] = None, 19 required: bool = True, 20 default: Any = None, 21 enum: Optional[list] = None, 22 default_present: bool = False, 23 ): 24 """Initialize a UnionParameter instance. 25 26 Args: 27 name (str): Name of the parameter. 28 options (List[Parameter]): List of Parameter instances representing the union types. 29 description (Optional[str]): Description of the parameter. 30 required (bool): Whether the parameter is required. 31 default (Any): Default value for the parameter. 32 enum (Optional[list]): Allowed values for the parameter. 33 default_present (bool): Whether a default value is explicitly set. 34 """ 35 super().__init__(name, description, required, default, enum, default_present) 36 self.options = options 37 for opt in options: 38 if isinstance(opt, UnionParameter): 39 raise TypeError( 40 "UnionParameter cannot contain another UnionParameter in its options" 41 ) 42 43 # param_type here is the list of inner types as strings, e.g. ["string", "null"] 44 # flatten and deduplicate types (order does not matter for schema) 45 flattened_types = [] 46 for opt in options: 47 pt = opt.param_type 48 if hasattr(pt, "value"): 49 pt = pt.__getattribute__("value") 50 if isinstance(pt, list): 51 flattened_types.extend(p for p in pt if p is not None) 52 elif pt is not None: 53 flattened_types.append(pt) 54 55 # Deduplicate while preserving order 56 self.param_type = list(set(flattened_types)) 57 58 def to_json_schema(self) -> Dict[str, Any]: 59 """Convert the union parameter to a JSON schema representation.""" 60 schema = { 61 "anyOf": [opt.to_json_schema() for opt in self.options], 62 } 63 64 if self.description: 65 schema["description"] = self.description # type: ignore 66 67 if self.default_present: 68 schema["default"] = self.default 69 70 return schema 71 72 def __repr__(self) -> str: 73 """Return a string representation of the UnionParameter.""" 74 return ( 75 f"UnionParameter(name={self.name!r}, options={self.options!r}, " 76 f"description={self.description!r}, required={self.required!r}, default={self.default!r})" 77 )
Parameter representing a union type.
14 def __init__( 15 self, 16 name: str, 17 options: List[Parameter], 18 description: Optional[str] = None, 19 required: bool = True, 20 default: Any = None, 21 enum: Optional[list] = None, 22 default_present: bool = False, 23 ): 24 """Initialize a UnionParameter instance. 25 26 Args: 27 name (str): Name of the parameter. 28 options (List[Parameter]): List of Parameter instances representing the union types. 29 description (Optional[str]): Description of the parameter. 30 required (bool): Whether the parameter is required. 31 default (Any): Default value for the parameter. 32 enum (Optional[list]): Allowed values for the parameter. 33 default_present (bool): Whether a default value is explicitly set. 34 """ 35 super().__init__(name, description, required, default, enum, default_present) 36 self.options = options 37 for opt in options: 38 if isinstance(opt, UnionParameter): 39 raise TypeError( 40 "UnionParameter cannot contain another UnionParameter in its options" 41 ) 42 43 # param_type here is the list of inner types as strings, e.g. ["string", "null"] 44 # flatten and deduplicate types (order does not matter for schema) 45 flattened_types = [] 46 for opt in options: 47 pt = opt.param_type 48 if hasattr(pt, "value"): 49 pt = pt.__getattribute__("value") 50 if isinstance(pt, list): 51 flattened_types.extend(p for p in pt if p is not None) 52 elif pt is not None: 53 flattened_types.append(pt) 54 55 # Deduplicate while preserving order 56 self.param_type = list(set(flattened_types))
Initialize a UnionParameter instance.
Arguments:
- name (str): Name of the parameter.
- options (List[Parameter]): List of Parameter instances representing the union types.
- description (Optional[str]): Description of the parameter.
- required (bool): Whether the parameter is required.
- default (Any): Default value for the parameter.
- enum (Optional[list]): Allowed values for the parameter.
- default_present (bool): Whether a default value is explicitly set.
58 def to_json_schema(self) -> Dict[str, Any]: 59 """Convert the union parameter to a JSON schema representation.""" 60 schema = { 61 "anyOf": [opt.to_json_schema() for opt in self.options], 62 } 63 64 if self.description: 65 schema["description"] = self.description # type: ignore 66 67 if self.default_present: 68 schema["default"] = self.default 69 70 return schema
Convert the union parameter to a JSON schema representation.
9class ArrayParameter(Parameter): 10 """Parameter representing an array type.""" 11 12 param_type: ParameterType = ParameterType.ARRAY 13 14 def __init__( 15 self, 16 name: str, 17 items: Parameter, 18 description: Optional[str] = None, 19 required: bool = True, 20 default: Any = None, 21 max_items: Optional[int] = None, 22 additional_properties: bool = False, 23 ): 24 """Initialize an ArrayParameter instance. 25 26 Args: 27 name (str): Name of the parameter. 28 items (Parameter): Parameter instance describing the type of array elements. 29 description (Optional[str]): Description of the parameter. 30 required (bool): Whether the parameter is required. 31 default (Any): Default value for the parameter. 32 max_items (Optional[int]): Maximum number of items allowed in the array. 33 additional_properties (bool): Whether additional properties are allowed (relevant if items are objects). 34 """ 35 super().__init__(name, description, required, default) 36 self.items = items 37 self.max_items = max_items 38 self.additional_properties = ( 39 additional_properties # might be relevant if items is object type 40 ) 41 42 def to_json_schema(self) -> Dict[str, Any]: 43 """Convert the array parameter to a JSON schema representation.""" 44 # Base property for items inside the array 45 items_schema = self.items.to_json_schema() 46 47 schema = { 48 "type": "array", 49 "items": items_schema, 50 } 51 if self.description: 52 schema["description"] = self.description 53 54 if self.max_items is not None: 55 schema["maxItems"] = self.max_items 56 57 # Set defaults and enum if present at the array level 58 if self.default is not None: 59 schema["default"] = self.default 60 61 # Note: enum on arrays is uncommon but if you want to support: 62 if self.enum: 63 schema["enum"] = self.enum 64 65 return schema 66 67 def __repr__(self) -> str: 68 """Return a string representation of the ArrayParameter.""" 69 return ( 70 f"ArrayParameter(name={self.name!r}, items={self.items!r}, " 71 f"description={self.description!r}, required={self.required!r}, " 72 f"default={self.default!r}, max_items={self.max_items!r}, " 73 f"additional_properties={self.additional_properties!r})" 74 )
Parameter representing an array type.
14 def __init__( 15 self, 16 name: str, 17 items: Parameter, 18 description: Optional[str] = None, 19 required: bool = True, 20 default: Any = None, 21 max_items: Optional[int] = None, 22 additional_properties: bool = False, 23 ): 24 """Initialize an ArrayParameter instance. 25 26 Args: 27 name (str): Name of the parameter. 28 items (Parameter): Parameter instance describing the type of array elements. 29 description (Optional[str]): Description of the parameter. 30 required (bool): Whether the parameter is required. 31 default (Any): Default value for the parameter. 32 max_items (Optional[int]): Maximum number of items allowed in the array. 33 additional_properties (bool): Whether additional properties are allowed (relevant if items are objects). 34 """ 35 super().__init__(name, description, required, default) 36 self.items = items 37 self.max_items = max_items 38 self.additional_properties = ( 39 additional_properties # might be relevant if items is object type 40 )
Initialize an ArrayParameter instance.
Arguments:
- name (str): Name of the parameter.
- items (Parameter): Parameter instance describing the type of array elements.
- description (Optional[str]): Description of the parameter.
- required (bool): Whether the parameter is required.
- default (Any): Default value for the parameter.
- max_items (Optional[int]): Maximum number of items allowed in the array.
- additional_properties (bool): Whether additional properties are allowed (relevant if items are objects).
42 def to_json_schema(self) -> Dict[str, Any]: 43 """Convert the array parameter to a JSON schema representation.""" 44 # Base property for items inside the array 45 items_schema = self.items.to_json_schema() 46 47 schema = { 48 "type": "array", 49 "items": items_schema, 50 } 51 if self.description: 52 schema["description"] = self.description 53 54 if self.max_items is not None: 55 schema["maxItems"] = self.max_items 56 57 # Set defaults and enum if present at the array level 58 if self.default is not None: 59 schema["default"] = self.default 60 61 # Note: enum on arrays is uncommon but if you want to support: 62 if self.enum: 63 schema["enum"] = self.enum 64 65 return schema
Convert the array parameter to a JSON schema representation.
9class ObjectParameter(Parameter): 10 """Parameter representing an object type.""" 11 12 param_type: ParameterType = ParameterType.OBJECT 13 14 def __init__( 15 self, 16 name: str, 17 properties: list[Parameter], 18 description: Optional[str] = None, 19 required: bool = True, 20 additional_properties: bool = False, 21 default: Any = None, 22 ): 23 """Initialize an ObjectParameter instance. 24 25 Args: 26 name (str): Name of the parameter. 27 properties (list[Parameter]): List of Parameter instances describing object properties. 28 description (Optional[str]): Description of the parameter. 29 required (bool): Whether the parameter is required. 30 additional_properties (bool): Whether additional properties are allowed. 31 default (Any): Default value for the parameter. 32 """ 33 super().__init__(name, description, required, default) 34 self.properties = properties 35 self.additional_properties = additional_properties 36 37 def to_json_schema(self) -> Dict[str, Any]: 38 """Convert the object parameter to a JSON schema representation.""" 39 schema = { 40 "type": "object", 41 "properties": {}, 42 "additionalProperties": self.additional_properties, 43 } 44 45 if self.description: 46 schema["description"] = self.description 47 48 required_props = [] 49 for prop in self.properties: 50 schema["properties"][prop.name] = prop.to_json_schema() 51 if prop.required: 52 required_props.append(prop.name) 53 54 if required_props: 55 schema["required"] = required_props 56 57 if self.default is not None: 58 schema["default"] = self.default 59 60 if self.enum: 61 schema["enum"] = self.enum 62 63 return schema 64 65 def __repr__(self) -> str: 66 """Return a string representation of the ObjectParameter.""" 67 return ( 68 f"ObjectParameter(name={self.name!r}, properties={self.properties!r}, " 69 f"description={self.description!r}, required={self.required!r}, " 70 f"additional_properties={self.additional_properties!r}, default={self.default!r})" 71 )
Parameter representing an object type.
14 def __init__( 15 self, 16 name: str, 17 properties: list[Parameter], 18 description: Optional[str] = None, 19 required: bool = True, 20 additional_properties: bool = False, 21 default: Any = None, 22 ): 23 """Initialize an ObjectParameter instance. 24 25 Args: 26 name (str): Name of the parameter. 27 properties (list[Parameter]): List of Parameter instances describing object properties. 28 description (Optional[str]): Description of the parameter. 29 required (bool): Whether the parameter is required. 30 additional_properties (bool): Whether additional properties are allowed. 31 default (Any): Default value for the parameter. 32 """ 33 super().__init__(name, description, required, default) 34 self.properties = properties 35 self.additional_properties = additional_properties
Initialize an ObjectParameter instance.
Arguments:
- name (str): Name of the parameter.
- properties (list[Parameter]): List of Parameter instances describing object properties.
- description (Optional[str]): Description of the parameter.
- required (bool): Whether the parameter is required.
- additional_properties (bool): Whether additional properties are allowed.
- default (Any): Default value for the parameter.
37 def to_json_schema(self) -> Dict[str, Any]: 38 """Convert the object parameter to a JSON schema representation.""" 39 schema = { 40 "type": "object", 41 "properties": {}, 42 "additionalProperties": self.additional_properties, 43 } 44 45 if self.description: 46 schema["description"] = self.description 47 48 required_props = [] 49 for prop in self.properties: 50 schema["properties"][prop.name] = prop.to_json_schema() 51 if prop.required: 52 required_props.append(prop.name) 53 54 if required_props: 55 schema["required"] = required_props 56 57 if self.default is not None: 58 schema["default"] = self.default 59 60 if self.enum: 61 schema["enum"] = self.enum 62 63 return schema
Convert the object parameter to a JSON schema representation.
9class RefParameter(Parameter): 10 """Parameter representing a reference type.""" 11 12 param_type: str = "object" # referenced schemas are always 'object' type 13 14 def __init__( 15 self, 16 name: str, 17 ref_path: str, 18 description: Optional[str] = None, 19 required: bool = True, 20 default: Any = None, 21 ): 22 """Initialize a RefParameter instance. 23 24 Args: 25 name (str): Name of the parameter. 26 ref_path (str): Reference path to the schema definition. 27 description (Optional[str]): Description of the parameter. 28 required (bool): Whether the parameter is required. 29 default (Any): Default value for the parameter. 30 """ 31 super().__init__(name, description, required, default) 32 self.ref_path = ref_path 33 34 def to_json_schema(self) -> Dict[str, Any]: 35 """Convert the reference parameter to a JSON schema representation.""" 36 schema = {"$ref": self.ref_path} 37 if self.description: 38 schema["description"] = self.description 39 40 if self.default is not None: 41 schema["default"] = self.default 42 43 if self.enum: 44 schema["enum"] = self.enum 45 46 return schema 47 48 def __repr__(self) -> str: 49 """Return a string representation of the RefParameter.""" 50 return ( 51 f"RefParameter(name={self.name!r}, ref_path={self.ref_path!r}, " 52 f"description={self.description!r}, required={self.required!r}, default={self.default!r})" 53 )
Parameter representing a reference type.
14 def __init__( 15 self, 16 name: str, 17 ref_path: str, 18 description: Optional[str] = None, 19 required: bool = True, 20 default: Any = None, 21 ): 22 """Initialize a RefParameter instance. 23 24 Args: 25 name (str): Name of the parameter. 26 ref_path (str): Reference path to the schema definition. 27 description (Optional[str]): Description of the parameter. 28 required (bool): Whether the parameter is required. 29 default (Any): Default value for the parameter. 30 """ 31 super().__init__(name, description, required, default) 32 self.ref_path = ref_path
Initialize a RefParameter instance.
Arguments:
- name (str): Name of the parameter.
- ref_path (str): Reference path to the schema definition.
- description (Optional[str]): Description of the parameter.
- required (bool): Whether the parameter is required.
- default (Any): Default value for the parameter.
34 def to_json_schema(self) -> Dict[str, Any]: 35 """Convert the reference parameter to a JSON schema representation.""" 36 schema = {"$ref": self.ref_path} 37 if self.description: 38 schema["description"] = self.description 39 40 if self.default is not None: 41 schema["default"] = self.default 42 43 if self.enum: 44 schema["enum"] = self.enum 45 46 return schema
Convert the reference parameter to a JSON schema representation.