///|
/// A request to the legacy `/completions` (text completion) endpoint.
///
/// This is the pre-chat completion API, still supported by some models and
/// many OpenAI-compatible gateways for raw prompt completion.
pub(all) struct CompletionRequest {
model : String
prompt : String
mut max_tokens : Int?
mut temperature : Double?
mut top_p : Double?
mut stop : Array[String]?
mut suffix : String?
}
///|
/// Create a completion request.
pub fn CompletionRequest::new(
model : String,
prompt : String,
) -> CompletionRequest {
{
model,
prompt,
max_tokens: None,
temperature: None,
top_p: None,
stop: None,
suffix: None,
}
}
///|
pub fn CompletionRequest::max_tokens(
self : CompletionRequest,
n : Int,
) -> CompletionRequest {
self.max_tokens = Some(n)
self
}
///|
pub fn CompletionRequest::temperature(
self : CompletionRequest,
t : Double,
) -> CompletionRequest {
self.temperature = Some(t)
self
}
///|
pub fn CompletionRequest::stop(
self : CompletionRequest,
s : Array[String],
) -> CompletionRequest {
self.stop = Some(s)
self
}
///|
pub impl ToJson for CompletionRequest with fn to_json(self : CompletionRequest) -> Json {
let obj : Map[String, Json] = {
"model": Json::string(self.model),
"prompt": Json::string(self.prompt),
}
if self.max_tokens is Some(n) {
obj["max_tokens"] = Json::number(n.to_double())
}
if self.temperature is Some(t) {
obj["temperature"] = Json::number(t)
}
if self.top_p is Some(p) {
obj["top_p"] = Json::number(p)
}
if self.stop is Some(s) {
obj["stop"] = s.to_json()
}
if self.suffix is Some(s) {
obj["suffix"] = Json::string(s)
}
Json::object(obj)
}
///|
/// A single completion choice.
pub(all) struct CompletionChoice {
index : Int
text : String
finish_reason : String?
} derive(Debug)
///|
pub impl @json.FromJson for CompletionChoice with fn from_json(
json : Json,
path : @json.JsonPath,
) -> CompletionChoice {
guard json is Object(obj) else {
raise @json.JsonDecodeError((path, "CompletionChoice: expected object"))
}
let index = match obj.get("index") {
Some(Number(n, ..)) => n.to_int()
_ => 0
}
let text = match obj.get("text") {
Some(String(s)) => s
_ => ""
}
let finish_reason = match obj.get("finish_reason") {
Some(String(s)) => Some(s)
_ => None
}
{ index, text, finish_reason }
}
///|
/// A response from the legacy `/completions` endpoint.
pub(all) struct CompletionResponse {
id : String
model : String
choices : Array[CompletionChoice]
usage : Usage?
} derive(Debug)
///|
pub impl @json.FromJson for CompletionResponse with fn from_json(
json : Json,
path : @json.JsonPath,
) -> CompletionResponse {
guard json is Object(obj) else {
raise @json.JsonDecodeError((path, "CompletionResponse: expected object"))
}
let id = match obj.get("id") {
Some(String(s)) => s
_ => ""
}
let model = match obj.get("model") {
Some(String(s)) => s
_ => ""
}
let choices = match obj.get("choices") {
Some(Array(_) as c) => @json.from_json(c)
_ => []
}
let usage = match obj.get("usage") {
Some(Object(_) as u) => Some(@json.from_json(u))
_ => None
}
{ id, model, choices, usage }
}
///|
/// The text of the first completion choice, if any.
pub fn CompletionResponse::text(self : CompletionResponse) -> String {
match self.choices.get(0) {
Some(c) => c.text
None => ""
}
}
///|
/// Perform a legacy text completion.
pub async fn Client::completion(
self : Client,
request : CompletionRequest,
) -> CompletionResponse raise LLMError {
let json = self.post_json("/completions", request.to_json())
@json.from_json(json) catch {
err => raise Decode(err.to_string())
}
}