artificial_openai/
provider_impl_chat.rs1use std::sync::Arc;
2
3use artificial_core::{
4 generic::{GenericChatCompletionResponse, GenericUsageReport, ResponseContent},
5 provider::{ChatCompleteParameters, ChatCompletionProvider},
6};
7
8use crate::{
9 OpenAiAdapter,
10 api_v1::{ChatCompletionMessage, FinishReason},
11 error::OpenAiError,
12};
13
14impl ChatCompletionProvider for OpenAiAdapter {
15 type Message = ChatCompletionMessage;
16
17 fn chat_complete<'s, M>(
18 &'s self,
19 params: ChatCompleteParameters<M>,
20 ) -> std::pin::Pin<
21 Box<
22 dyn Future<
23 Output = artificial_core::error::Result<
24 artificial_core::generic::GenericChatCompletionResponse<
25 artificial_core::generic::GenericMessage,
26 >,
27 >,
28 > + Send
29 + 's,
30 >,
31 >
32 where
33 M: Into<Self::Message> + Clone + Send + Sync + 's,
34 {
35 let client = Arc::clone(&self.client);
36
37 Box::pin(async move {
38 let request = params.try_into()?;
39
40 let mut response = client.chat_completion(request).await?;
41
42 let usage_report = GenericUsageReport {
43 prompt_tokens: response.usage.prompt_tokens as i64,
44 completion_tokens: response.usage.completion_tokens as i64,
45 total_tokens: response.usage.total_tokens as i64,
46 };
47
48 let Some(first_choice) = response.choices.pop() else {
49 return Err(OpenAiError::Format("response has no choices".into()).into());
50 };
51
52 match &first_choice.finish_reason {
53 Some(FinishReason::ToolCalls) => {
54 let response = GenericChatCompletionResponse {
55 content: ResponseContent::ToolCalls(first_choice.message.into()),
56 usage: Some(usage_report),
57 };
58 Ok(response)
59 }
60 None | Some(FinishReason::Stop) => {
61 let response = GenericChatCompletionResponse {
62 content: ResponseContent::Finished(first_choice.message.into()),
63 usage: Some(usage_report),
64 };
65 Ok(response)
66 }
67 Some(other) => Err(OpenAiError::Format(format!(
68 "unhandled finish reason on API: {other:?}"
69 ))
70 .into()),
71 }
72 })
73 }
74}