1+ import platform
12import time
23import uuid
34import httpx
45import os
56from typing import (Optional , Dict , Union , List )
6- from pydantic import (BaseModel , validator , Field )
7+ from pydantic import (BaseModel , Field , ConfigDict , model_validator )
8+ from .utils import get_version
79from .errors import InfuzuAPIError
810
911
1012class ModelWeights (BaseModel ):
13+ model_config = ConfigDict (extra = "allow" )
14+
1115 price : Optional [float ] = None
1216 error : Optional [float ] = None
1317 start_latency : Optional [float ] = None
1418 end_latency : Optional [float ] = None
1519
16- class Config :
17- extra : str = "allow"
18-
1920
2021class InfuzuModelParams (BaseModel ):
22+ model_config = ConfigDict (extra = "allow" )
23+
2124 llms : Optional [List [str ]] = None
2225 exclude_llms : Optional [List [str ]] = None
2326 weights : Optional [ModelWeights ] = None
2427 imsn : Optional [int ] = None
2528 max_input_cost : Optional [float ] = None
2629 max_output_cost : Optional [float ] = None
2730
28- class Config :
29- extra : str = "allow"
30-
3131
3232class ChatCompletionsRequestContentPart (BaseModel ):
33+ model_config = ConfigDict (extra = "allow" )
34+
3335 type : str
3436 text : Optional [str ] = None
3537 image_url : Optional [str ] = None
3638 input_audio : Optional [str ] = None
3739
38- class Config :
39- extra : str = "allow"
40-
41- @validator ("text" , always = True )
42- def check_content_fields (cls , value , values ):
43- if "type" in values :
44- content_type = values ["type" ]
45- if content_type == "text" and value is None :
46- raise ValueError ("Text must be provided when type is 'text'" )
47- if content_type != "text" and value is not None :
48- raise ValueError ("Text cannot be provided when type is not 'text'" )
49- return value
40+ @model_validator (mode = 'after' )
41+ def check_content_fields (self ) -> 'ChatCompletionsRequestContentPart' :
42+ if self .type == "text" and self .text is None :
43+ raise ValueError ("Text must be provided when type is 'text'" )
44+ if self .type != "text" and self .text is not None :
45+ raise ValueError ("Text cannot be provided when type is not 'text'" )
46+ return self
5047
5148
5249class ChatCompletionsHandlerRequestMessage (BaseModel ):
50+ model_config = ConfigDict (extra = "allow" )
51+
5352 content : Union [str , List [ChatCompletionsRequestContentPart ]]
5453 role : str
5554 name : Optional [str ] = None
5655
57- class Config :
58- extra : str = "allow"
59-
60- @validator ('role' )
61- def role_must_be_valid (cls , v ):
62- if v not in ('system' , 'user' , 'assistant' ):
56+ @model_validator (mode = 'after' )
57+ def role_must_be_valid (self ) -> 'ChatCompletionsHandlerRequestMessage' :
58+ if self .role not in ('system' , 'user' , 'assistant' ):
6359 raise ValueError ('Role must be one of: system, user, assistant' )
64- return v
60+ return self
6561
6662
6763class ChatCompletionsChoiceMessageAudioObject (BaseModel ):
64+ model_config = ConfigDict (extra = "allow" )
65+
6866 id : Optional [str ] = None
6967 expired_at : Optional [int ] = None
7068 data : Optional [str ] = None
7169 transcript : Optional [str ] = None
7270
73- class Config :
74- extra : str = "allow"
75-
7671
7772class ChatCompletionsChoiceMessageFunctionCallObject (BaseModel ):
73+ model_config = ConfigDict (extra = "allow" )
74+
7875 name : Optional [str ] = None
7976 arguments : Optional [str ] = None
8077
81- class Config :
82- extra : str = "allow"
83-
8478
8579class ChatCompletionsChoiceMessageToolCallFunctionObject (BaseModel ):
80+ model_config = ConfigDict (extra = "allow" )
81+
8682 name : Optional [str ] = None
8783 arguments : Optional [str ] = None
8884
89- class Config :
90- extra : str = "allow"
91-
9285
9386class chatCompletionsChoiceMessageToolCallObject (BaseModel ):
87+ model_config = ConfigDict (extra = "allow" )
88+
9489 id : Optional [str ] = None
9590 type : Optional [str ] = None
9691 function : Optional [ChatCompletionsChoiceMessageToolCallFunctionObject ] = None
9792
98- class Config :
99- extra : str = "allow"
100-
10193
10294class ChatCompletionsChoiceMessageObject (BaseModel ):
95+ model_config = ConfigDict (extra = "allow" )
96+
10397 content : Optional [str ] = None
10498 refusal : Optional [str ] = None
10599 tool_calls : Optional [List [chatCompletionsChoiceMessageToolCallObject ]] = None
106100 role : Optional [str ] = None
107101 function_call : Optional [ChatCompletionsChoiceMessageFunctionCallObject ] = None
108102 audio : Optional [ChatCompletionsChoiceMessageAudioObject ] = None
109103
110- class Config :
111- extra : str = "allow"
112-
113104
114105class ChatCompletionsChoiceLogprobsItemTopLogprobObject (BaseModel ):
106+ model_config = ConfigDict (extra = "allow" )
107+
115108 token : Optional [str ] = None
116109 logprob : Optional [int ] = None
117110 bytes : Optional [List [int ]] = None
118111
119- class Config :
120- extra : str = "allow"
121-
122112
123113class ChatCompletionsLogprobsItemObject (BaseModel ):
114+ model_config = ConfigDict (extra = "allow" )
115+
124116 token : Optional [str ] = None
125117 logprob : Optional [int ] = None
126118 bytes : Optional [List [int ]] = None
127119 content : Optional [List [ChatCompletionsChoiceLogprobsItemTopLogprobObject ]] = None
128120
129- class Config :
130- extra : str = "allow"
131-
132121
133122class ChatCompletionsChoiceLogprobsObject (BaseModel ):
123+ model_config = ConfigDict (extra = "allow" )
124+
134125 content : Optional [List [ChatCompletionsLogprobsItemObject ]] = None
135126 refusal : Optional [List [ChatCompletionsLogprobsItemObject ]] = None
136127
137- class Config :
138- extra : str = "allow"
139-
140128
141129class ChatCompletionsChoiceModelObject (BaseModel ):
130+ model_config = ConfigDict (extra = "allow" )
131+
142132 ref : Optional [str ] = None
143133 rank : Optional [int ] = None
144134
145- class Config :
146- extra : str = "allow"
147-
148135
149136class ChatCompletionsChoiceErrorObject (BaseModel ):
137+ model_config = ConfigDict (extra = "allow" )
138+
150139 message : Optional [str ] = None
151140 code : Optional [str ] = None
152141
153- class Config :
154- extra : str = "allow"
155-
156142
157143class ChatCompletionsChoiceLatencyObject (BaseModel ):
144+ model_config = ConfigDict (extra = "allow" )
145+
158146 start : Optional [int ] = Field (None , alias = 'start_latency' )
159147 end : Optional [int ] = Field (None , alias = 'end_latency' )
160148
161- class Config :
162- extra : str = "allow"
163-
164149
165150class ChatCompletionsChoiceObject (BaseModel ):
151+ model_config = ConfigDict (extra = "allow" )
152+
166153 finish_reason : Optional [str ] = None
167154 index : Optional [int ] = None
168155 message : Optional [ChatCompletionsChoiceMessageObject ] = None
@@ -171,11 +158,10 @@ class ChatCompletionsChoiceObject(BaseModel):
171158 error : Optional [ChatCompletionsChoiceErrorObject ] = None
172159 latency : Optional [ChatCompletionsChoiceLatencyObject ] = None
173160
174- class Config :
175- extra : str = "allow"
176-
177161
178162class ChatCompletionsObject (BaseModel ):
163+ model_config = ConfigDict (extra = "allow" )
164+
179165 id : Optional [str ] = None
180166 choices : Optional [List [ChatCompletionsChoiceObject ]] = None
181167 created : Optional [int ] = None
@@ -185,11 +171,8 @@ class ChatCompletionsObject(BaseModel):
185171 object : Optional [str ] = None
186172 usage : Optional [Dict [str , int ]] = None
187173
188- class Config :
189- extra : str = "allow"
190-
191174
192- API_BASE_URL = "https://chat.infuzu.com/api"
175+ API_BASE_URL : str = "https://chat.infuzu.com/api"
193176
194177
195178def create_chat_completion (
@@ -204,43 +187,49 @@ def create_chat_completion(
204187 messages: A list of message objects.
205188 api_key: Your Infuzu API key. If not provided, it will be read from the
206189 INFUZU_API_KEY environment variable.
207- model: The model to use for the chat completion. Can be a string (model name)
190+ model: The model to use for the chat completion. Can be a string (model name)
208191 or a InfuzuModelParams object for more advanced configuration.
209192
210193 Returns:
211- A dictionary containing the JSON response from the API.
194+ The ChatCompletionsObject Object
212195
213196 Raises:
214197 ValueError: If the API key is not provided and the INFUZU_API_KEY
215198 environment variable is not set.
216- httpx.HTTPStatusError : If the API request returns an error status code.
199+ InfuzuAPIError : If the API request returns an error status code.
217200 """
218201
219202 if api_key is None :
220- api_key = os .environ .get ("INFUZU_API_KEY" )
203+ api_key : str | None = os .environ .get ("INFUZU_API_KEY" )
221204 if api_key is None :
222205 raise ValueError (
223206 "API key not provided and INFUZU_API_KEY environment variable not set."
224207 )
225208
226- headers = {
209+ headers : dict [ str , str ] = {
227210 "Content-Type" : "application/json" ,
228211 "Infuzu-API-Key" : api_key ,
212+ "User-Agent" : (
213+ f"infuzu-python/{ get_version ()} "
214+ f"(Python { platform .python_version ()} ; "
215+ f"httpx/{ httpx .__version__ } ; "
216+ f"{ platform .system ()} { platform .release ()} )"
217+ )
229218 }
230219
231- payload = {
232- "messages" : [message .dict (by_alias = True ) for message in messages ],
220+ payload : dict [ str , any ] = {
221+ "messages" : [message .model_dump (by_alias = True ) for message in messages ],
233222 }
234223
235224 if model :
236225 if isinstance (model , str ):
237226 payload ["model" ] = model
238227 else :
239- payload ["model" ] = model .dict (by_alias = True )
228+ payload ["model" ] = model .model_dump (by_alias = True )
240229
241230 try :
242231 with httpx .Client () as client :
243- response = client .post (
232+ response : httpx . Response = client .post (
244233 f"{ API_BASE_URL } /v1/chat/completions" ,
245234 headers = headers ,
246235 json = payload ,
0 commit comments