artificial_openai/
provider_impl_chat.rs

1use 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}