1616
1717package  org .springframework .ai .azure .openai ;
1818
19- import  java .util .ArrayList ;
20- import  java .util .Base64 ;
21- import  java .util .Collections ;
22- import  java .util .HashSet ;
23- import  java .util .List ;
24- import  java .util .Map ;
25- import  java .util .Optional ;
26- import  java .util .Set ;
27- import  java .util .concurrent .atomic .AtomicBoolean ;
28- 
19+ import  com .azure .ai .openai .OpenAIAsyncClient ;
20+ import  com .azure .ai .openai .OpenAIClient ;
21+ import  com .azure .ai .openai .OpenAIClientBuilder ;
22+ import  com .azure .ai .openai .models .*;
23+ import  com .azure .core .util .BinaryData ;
2924import  org .springframework .ai .azure .openai .metadata .AzureOpenAiUsage ;
3025import  org .springframework .ai .chat .messages .AssistantMessage ;
3126import  org .springframework .ai .chat .messages .Message ;
4944import  org .springframework .ai .model .function .FunctionCallbackContext ;
5045import  org .springframework .util .Assert ;
5146import  org .springframework .util .CollectionUtils ;
52- 
53- import  com .azure .ai .openai .OpenAIClient ;
54- import  com .azure .ai .openai .models .ChatChoice ;
55- import  com .azure .ai .openai .models .ChatCompletions ;
56- import  com .azure .ai .openai .models .ChatCompletionsFunctionToolCall ;
57- import  com .azure .ai .openai .models .ChatCompletionsFunctionToolDefinition ;
58- import  com .azure .ai .openai .models .ChatCompletionsJsonResponseFormat ;
59- import  com .azure .ai .openai .models .ChatCompletionsOptions ;
60- import  com .azure .ai .openai .models .ChatCompletionsResponseFormat ;
61- import  com .azure .ai .openai .models .ChatCompletionsTextResponseFormat ;
62- import  com .azure .ai .openai .models .ChatCompletionsToolCall ;
63- import  com .azure .ai .openai .models .ChatCompletionsToolDefinition ;
64- import  com .azure .ai .openai .models .ChatMessageContentItem ;
65- import  com .azure .ai .openai .models .ChatMessageImageContentItem ;
66- import  com .azure .ai .openai .models .ChatMessageImageUrl ;
67- import  com .azure .ai .openai .models .ChatMessageTextContentItem ;
68- import  com .azure .ai .openai .models .ChatRequestAssistantMessage ;
69- import  com .azure .ai .openai .models .ChatRequestMessage ;
70- import  com .azure .ai .openai .models .ChatRequestSystemMessage ;
71- import  com .azure .ai .openai .models .ChatRequestToolMessage ;
72- import  com .azure .ai .openai .models .ChatRequestUserMessage ;
73- import  com .azure .ai .openai .models .CompletionsFinishReason ;
74- import  com .azure .ai .openai .models .ContentFilterResultsForPrompt ;
75- import  com .azure .ai .openai .models .FunctionCall ;
76- import  com .azure .ai .openai .models .FunctionDefinition ;
77- import  com .azure .core .util .BinaryData ;
78- import  com .azure .core .util .IterableStream ;
79- 
8047import  reactor .core .publisher .Flux ;
8148import  reactor .core .publisher .Mono ;
8249
50+ import  java .util .ArrayList ;
51+ import  java .util .Base64 ;
52+ import  java .util .Collections ;
53+ import  java .util .HashSet ;
54+ import  java .util .List ;
55+ import  java .util .Map ;
56+ import  java .util .Optional ;
57+ import  java .util .Set ;
58+ import  java .util .concurrent .atomic .AtomicBoolean ;
59+ 
8360/** 
8461 * {@link ChatModel} implementation for {@literal Microsoft Azure AI} backed by 
8562 * {@link OpenAIClient}. 
9673 * @author Soby Chacko 
9774 * @see ChatModel 
9875 * @see com.azure.ai.openai.OpenAIClient 
76+  * @since 1.0.0 
9977 */ 
10078public  class  AzureOpenAiChatModel  extends  AbstractToolCallSupport  implements  ChatModel  {
10179
@@ -108,34 +86,40 @@ public class AzureOpenAiChatModel extends AbstractToolCallSupport implements Cha
10886	 */ 
10987	private  final  OpenAIClient  openAIClient ;
11088
89+ 	/** 
90+ 	 * The {@link OpenAIAsyncClient} used for streaming async operations. 
91+ 	 */ 
92+ 	private  final  OpenAIAsyncClient  openAIAsyncClient ;
93+ 
11194	/** 
11295	 * The configuration information for a chat completions request. 
11396	 */ 
114- 	private  AzureOpenAiChatOptions  defaultOptions ;
97+ 	private  final   AzureOpenAiChatOptions  defaultOptions ;
11598
116- 	public  AzureOpenAiChatModel (OpenAIClient  microsoftOpenAiClient ) {
99+ 	public  AzureOpenAiChatModel (OpenAIClientBuilder  microsoftOpenAiClient ) {
117100		this (microsoftOpenAiClient ,
118101				AzureOpenAiChatOptions .builder ()
119102					.withDeploymentName (DEFAULT_DEPLOYMENT_NAME )
120103					.withTemperature (DEFAULT_TEMPERATURE )
121104					.build ());
122105	}
123106
124- 	public  AzureOpenAiChatModel (OpenAIClient   microsoftOpenAiClient , AzureOpenAiChatOptions  options ) {
125- 		this (microsoftOpenAiClient , options , null );
107+ 	public  AzureOpenAiChatModel (OpenAIClientBuilder   openAIClientBuilder , AzureOpenAiChatOptions  options ) {
108+ 		this (openAIClientBuilder , options , null );
126109	}
127110
128- 	public  AzureOpenAiChatModel (OpenAIClient   microsoftOpenAiClient , AzureOpenAiChatOptions  options ,
111+ 	public  AzureOpenAiChatModel (OpenAIClientBuilder   openAIClientBuilder , AzureOpenAiChatOptions  options ,
129112			FunctionCallbackContext  functionCallbackContext ) {
130- 		this (microsoftOpenAiClient , options , functionCallbackContext , List .of ());
113+ 		this (openAIClientBuilder , options , functionCallbackContext , List .of ());
131114	}
132115
133- 	public  AzureOpenAiChatModel (OpenAIClient   microsoftOpenAiClient , AzureOpenAiChatOptions  options ,
116+ 	public  AzureOpenAiChatModel (OpenAIClientBuilder   openAIClientBuilder , AzureOpenAiChatOptions  options ,
134117			FunctionCallbackContext  functionCallbackContext , List <FunctionCallback > toolFunctionCallbacks ) {
135118		super (functionCallbackContext , options , toolFunctionCallbacks );
136- 		Assert .notNull (microsoftOpenAiClient , "com.azure.ai.openai.OpenAIClient must not be null" );
119+ 		Assert .notNull (openAIClientBuilder , "com.azure.ai.openai.OpenAIClient must not be null" );
137120		Assert .notNull (options , "AzureOpenAiChatOptions must not be null" );
138- 		this .openAIClient  = microsoftOpenAiClient ;
121+ 		this .openAIClient  = openAIClientBuilder .buildClient ();
122+ 		this .openAIAsyncClient  = openAIClientBuilder .buildAsyncClient ();
139123		this .defaultOptions  = options ;
140124	}
141125
@@ -170,11 +154,11 @@ public Flux<ChatResponse> stream(Prompt prompt) {
170154		ChatCompletionsOptions  options  = toAzureChatCompletionsOptions (prompt );
171155		options .setStream (true );
172156
173- 		IterableStream <ChatCompletions > chatCompletionsStream  = this .openAIClient 
157+ 		Flux <ChatCompletions > chatCompletionsStream  = this .openAIAsyncClient 
174158			.getChatCompletionsStream (options .getModel (), options );
175159
176160		final  var  isFunctionCall  = new  AtomicBoolean (false );
177- 		final  Flux <ChatCompletions > accessibleChatCompletionsFlux  = Flux . fromIterable ( chatCompletionsStream ) 
161+ 		final  Flux <ChatCompletions > accessibleChatCompletionsFlux  = chatCompletionsStream 
178162			// Note: the first chat completions can be ignored when using Azure OpenAI 
179163			// service which is a known service bug. 
180164			.filter (chatCompletions  -> !CollectionUtils .isEmpty (chatCompletions .getChoices ()))
@@ -254,15 +238,13 @@ public static ChatResponseMetadata from(ChatCompletions chatCompletions, PromptM
254238		Assert .notNull (chatCompletions , "Azure OpenAI ChatCompletions must not be null" );
255239		String  id  = chatCompletions .getId ();
256240		Usage  usage  = (chatCompletions .getUsage () != null ) ? AzureOpenAiUsage .from (chatCompletions ) : new  EmptyUsage ();
257- 		ChatResponseMetadata   chatResponseMetadata  =  ChatResponseMetadata .builder ()
241+ 		return  ChatResponseMetadata .builder ()
258242			.withId (id )
259243			.withUsage (usage )
260244			.withModel (chatCompletions .getModel ())
261245			.withPromptMetadata (promptFilterMetadata )
262246			.withKeyValue ("system-fingerprint" , chatCompletions .getSystemFingerprint ())
263247			.build ();
264- 
265- 		return  chatResponseMetadata ;
266248	}
267249
268250	/** 
0 commit comments