1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use eventsource_client::{Client as EsClient, ClientBuilder, ReconnectOptions, SSE};
use futures::stream::{Stream, TryStreamExt};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use crate::error::Error;
// Fill in the Middle Completion API
const FIM_API: &str = "/fim/completions";
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct MessageBody {
/// ID of the model to use. You can use the [List Available Models API](https://docs.mistral.ai/api/#tag/models/operation/list_models_v1_models_get) to see all of your available models, or see our [Model overview](https://docs.mistral.ai/models) for model descriptions.
pub model: String,
/// What sampling temperature to use, between 0.0 and 1.0. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or top_p but not both.
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
/// Nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
/// The maximum number of tokens to generate in the completion. The token count of your prompt plus max_tokens cannot exceed the model's context length.
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
/// The minimum number of tokens to generate in the completion.
#[serde(skip_serializing_if = "Option::is_none")]
pub min_tokens: Option<u32>,
/// Stop generation if this token is detected. Or if one of these tokens is detected when providing an array
#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Vec<String>>,
/// Whether to stream back partial progress. If set, tokens will be sent as data-only server-side events as they become available, with the stream terminated by a data: [DONE] message. Otherwise, the server will hold the request open until the timeout or until completion, with the response containing the full result as JSON.
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
/// The seed to use for random sampling. If set, different calls will generate deterministic results.
#[serde(skip_serializing_if = "Option::is_none")]
pub random_seed: Option<u32>,
/// The text/code to complete.
pub prompt: String,
/// Optional text/code that adds more context for the model. When given a prompt and a suffix the model will fill what is between them. When suffix is not provided, the model will simply execute completion starting with prompt.
#[serde(skip_serializing_if = "Option::is_none")]
pub suffix: Option<String>,
}
impl MessageBody {
/// Creates a new `MessageBody`
#[must_use]
pub fn new(model: &str, prompt: String, suffix: Option<String>) -> Self {
Self {
model: model.into(),
prompt,
suffix,
stream: Some(true),
..Default::default()
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FimCompletionsChunk {
pub id: String,
pub object: String,
pub created: u64,
pub model: String,
pub choices: Vec<Choice>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Choice {
pub index: u32,
pub delta: Delta,
pub finish_reason: Option<String>,
pub logprobs: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Delta {
pub content: String,
}
pub use crate::mistral::Auth;
#[derive(Debug, Clone)]
pub struct Client {
pub auth: Auth,
pub api_url: String,
}
impl Client {
pub fn new(auth: Auth, api_url: impl Into<String>) -> Self {
Self {
auth,
api_url: api_url.into(),
}
}
}
impl Client {
pub fn delta<'a>(
&'a self,
message_body: &'a MessageBody,
) -> Result<impl Stream<Item = Result<String, Error>> + 'a, Error> {
log::debug!("message_body: {:#?}", message_body);
let request_body = match serde_json::to_value(message_body) {
Ok(body) => body,
Err(e) => return Err(Error::Serde(e)),
};
log::debug!("request_body: {:#?}", request_body);
let authorization: &str = &format!("Bearer {}", self.auth.api_key);
let client = ClientBuilder::for_url(&(self.api_url.clone() + FIM_API))?
.header("content-type", "application/json")?
.header("authorization", authorization)?
.method("POST".into())
.body(request_body.to_string())
.reconnect(
ReconnectOptions::reconnect(true)
.retry_initial(false)
.delay(Duration::from_secs(1))
.backoff_factor(2)
.delay_max(Duration::from_secs(60))
.build(),
)
.build();
let stream = Box::pin(client.stream())
.map_err(Error::from)
.map_ok(|event| match event {
SSE::Connected(_) => String::default(),
SSE::Event(ev) => match serde_json::from_str::<FimCompletionsChunk>(&ev.data) {
Ok(chunk) => {
if chunk.choices.is_empty() {
String::default()
} else {
chunk.choices.first().unwrap().delta.content.clone()
}
}
Err(_) => String::default(),
},
SSE::Comment(comment) => {
log::debug!("Comment: {:#?}", comment);
String::default()
}
});
Ok(stream)
}
}