Skip to content

Commit 20a2502

Browse files
committed
Add builders and docs for fluent-sdk
1 parent 97ecec3 commit 20a2502

5 files changed

Lines changed: 119 additions & 3 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
Fluent CLI is a powerful command-line interface for interacting with various AI engines. It provides a unified way to send requests, receive responses, and manage interactions with different AI services.
1414

15+
For programmatic access, check out the [`fluent-sdk` crate](https://crates.io/crates/fluent-sdk).
16+
1517
### Installation
1618

1719
Fluent CLI requires Rust and Cargo to be installed on your system. You can install them using the instructions on the [Rust website](https://www.rust-lang.org/tools/install).

crates/fluent-sdk/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
name = "fluent-sdk"
33
version = "0.1.0"
44
edition = "2021"
5+
description = "SDK for interacting with the Fluent AI engine configurations"
6+
license = "MIT"
7+
repository = "https://github.com/njfio/fluent_cli"
8+
readme = "../../README.md"
9+
homepage = "https://fluentcli.com"
510

611

712
[dependencies]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use fluent_sdk::prelude::*;
2+
3+
#[tokio::main]
4+
async fn main() -> anyhow::Result<()> {
5+
let request = FluentOpenAIChatRequest::builder()
6+
.prompt("Hello, world!".to_string())
7+
.openai_key("YOUR_OPENAI_KEY".to_string())
8+
.build()?;
9+
10+
let response = request.run().await?;
11+
println!("{:?}", response.data);
12+
Ok(())
13+
}

crates/fluent-sdk/src/lib.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
//! Fluent SDK provides strongly typed builders for composing requests to the
2+
//! various engines supported by Fluent.
3+
//!
4+
//! Most users will interact with the [`FluentOpenAIChatRequestBuilder`], but a
5+
//! generic [`FluentRequestBuilder`] is also available when you need full
6+
//! control.
7+
18
use anyhow::anyhow;
29
use serde::{Deserialize, Serialize};
310
use serde_json::Value;
@@ -38,6 +45,10 @@ pub struct FluentRequest {
3845
pub parse_code: Option<bool>,
3946
}
4047
impl FluentRequest {
48+
/// Creates a new [`FluentRequestBuilder`].
49+
pub fn builder() -> FluentRequestBuilder {
50+
FluentRequestBuilder::default()
51+
}
4152
pub async fn run(&self) -> anyhow::Result<Response> {
4253
// Convert the implementing type into a FluentRequest
4354
let request = self.clone();
@@ -88,6 +99,71 @@ impl FluentRequest {
8899
}
89100
}
90101

102+
/// Builder for [`FluentRequest`].
103+
#[derive(Debug, Clone)]
104+
pub struct FluentRequestBuilder {
105+
request: FluentRequest,
106+
}
107+
108+
impl Default for FluentRequestBuilder {
109+
fn default() -> Self {
110+
Self {
111+
request: FluentRequest {
112+
engine: None,
113+
credentials: Some(Vec::new()),
114+
overrides: Some(HashMap::new()),
115+
request: None,
116+
parse_code: Some(false),
117+
},
118+
}
119+
}
120+
}
121+
122+
impl FluentRequestBuilder {
123+
/// Sets the engine template to use.
124+
pub fn engine(mut self, engine: EngineTemplate) -> Self {
125+
self.request.engine = Some(engine);
126+
self
127+
}
128+
129+
/// Sets the request string that will be sent to the engine.
130+
pub fn request(mut self, request: impl Into<String>) -> Self {
131+
self.request.request = Some(request.into());
132+
self
133+
}
134+
135+
/// Adds a credential key/value pair.
136+
pub fn credential(mut self, kv: impl Into<KeyValue>) -> Self {
137+
let entry = self.request.credentials.get_or_insert_with(Vec::new);
138+
entry.push(kv.into());
139+
self
140+
}
141+
142+
/// Adds a single override parameter.
143+
pub fn override_param(mut self, key: impl Into<String>, value: Value) -> Self {
144+
let map = self.request.overrides.get_or_insert_with(HashMap::new);
145+
map.insert(key.into(), value);
146+
self
147+
}
148+
149+
/// Whether to parse code blocks from the engine output.
150+
pub fn parse_code(mut self, parse: bool) -> Self {
151+
self.request.parse_code = Some(parse);
152+
self
153+
}
154+
155+
/// Finalises the builder returning a [`FluentRequest`].
156+
pub fn build(self) -> anyhow::Result<FluentRequest> {
157+
if self.request.engine.is_none() {
158+
return Err(anyhow!("Engine is required"));
159+
}
160+
if self.request.request.is_none() {
161+
return Err(anyhow!("Request is required"));
162+
}
163+
Ok(self.request)
164+
}
165+
}
166+
91167
#[derive(Debug, PartialEq, EnumString, Serialize, Deserialize, Display, Clone)]
92168
pub enum EngineTemplate {
93169
#[strum(ascii_case_insensitive, to_string = "openai-chat-completions")]

crates/fluent-sdk/src/openai.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{EngineTemplate, FluentRequest, FluentSdkRequest, KeyValue};
66

77
impl FluentSdkRequest for FluentOpenAIChatRequest {}
88

9+
/// Request data used for OpenAI chat completions.
910
#[derive(Debug, Deserialize, Serialize, Clone)]
1011
pub struct FluentOpenAIChatRequest {
1112
pub prompt: String,
@@ -20,6 +21,12 @@ pub struct FluentOpenAIChatRequest {
2021
pub frequency_penalty: Option<f64>,
2122
pub presence_penalty: Option<f64>,
2223
}
24+
impl FluentOpenAIChatRequest {
25+
/// Creates a new [`FluentOpenAIChatRequestBuilder`].
26+
pub fn builder() -> FluentOpenAIChatRequestBuilder {
27+
FluentOpenAIChatRequestBuilder::default()
28+
}
29+
}
2330
impl From<FluentOpenAIChatRequest> for FluentRequest {
2431
fn from(request: FluentOpenAIChatRequest) -> Self {
2532
let mut overrides = vec![];
@@ -60,6 +67,7 @@ impl From<FluentOpenAIChatRequest> for FluentRequest {
6067
}
6168
}
6269

70+
/// Builder for [`FluentOpenAIChatRequest`].
6371
#[derive(Debug, Deserialize, Serialize, Clone)]
6472
pub struct FluentOpenAIChatRequestBuilder {
6573
request: FluentOpenAIChatRequest,
@@ -71,12 +79,12 @@ impl Default for FluentOpenAIChatRequestBuilder {
7179
prompt: String::new(),
7280
openai_key: String::new(),
7381
response_format: None,
74-
temperature: None,
82+
temperature: Some(0.7),
7583
max_tokens: None,
76-
top_p: None,
84+
top_p: Some(1.0),
7785
frequency_penalty: None,
7886
presence_penalty: None,
79-
model: None,
87+
model: Some("gpt-3.5-turbo".to_string()),
8088
n: None,
8189
stop: None,
8290
},
@@ -85,50 +93,62 @@ impl Default for FluentOpenAIChatRequestBuilder {
8593
}
8694

8795
impl FluentOpenAIChatRequestBuilder {
96+
/// Sets the user prompt.
8897
pub fn prompt(mut self, prompt: String) -> Self {
8998
self.request.prompt = prompt;
9099
self
91100
}
101+
/// Sets the OpenAI API key.
92102
pub fn openai_key(mut self, openai_key: String) -> Self {
93103
self.request.openai_key = openai_key;
94104
self
95105
}
106+
/// Overrides the `response_format` parameter.
96107
pub fn response_format(mut self, response_format: Value) -> Self {
97108
self.request.response_format = Some(response_format);
98109
self
99110
}
111+
/// Overrides the `temperature` parameter.
100112
pub fn temperature(mut self, temperature: f64) -> Self {
101113
self.request.temperature = Some(temperature);
102114
self
103115
}
116+
/// Overrides the `max_tokens` parameter.
104117
pub fn max_tokens(mut self, max_tokens: i64) -> Self {
105118
self.request.max_tokens = Some(max_tokens);
106119
self
107120
}
121+
/// Overrides the `top_p` parameter.
108122
pub fn top_p(mut self, top_p: f64) -> Self {
109123
self.request.top_p = Some(top_p);
110124
self
111125
}
126+
/// Overrides the `frequency_penalty` parameter.
112127
pub fn frequency_penalty(mut self, frequency_penalty: f64) -> Self {
113128
self.request.frequency_penalty = Some(frequency_penalty);
114129
self
115130
}
131+
/// Overrides the `presence_penalty` parameter.
116132
pub fn presence_penalty(mut self, presence_penalty: f64) -> Self {
117133
self.request.presence_penalty = Some(presence_penalty);
118134
self
119135
}
136+
/// Overrides the model to use.
120137
pub fn model(mut self, model: String) -> Self {
121138
self.request.model = Some(model);
122139
self
123140
}
141+
/// Overrides the `n` parameter.
124142
pub fn n(mut self, n: i8) -> Self {
125143
self.request.n = Some(n);
126144
self
127145
}
146+
/// Overrides the `stop` parameter.
128147
pub fn stop(mut self, stop: Vec<String>) -> Self {
129148
self.request.stop = Some(stop);
130149
self
131150
}
151+
/// Builds the request returning an error if required fields are missing.
132152
pub fn build(self) -> anyhow::Result<FluentOpenAIChatRequest> {
133153
if self.request.prompt.is_empty() {
134154
return Err(anyhow!("Prompt is required"));

0 commit comments

Comments
 (0)