Skip to content

Commit fcd0d15

Browse files
Qardclaude
andcommitted
Add Prompts API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a3880bf commit fcd0d15

5 files changed

Lines changed: 668 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ categories = ["api-bindings", "asynchronous", "development-tools"]
1313
[dependencies]
1414
anyhow = "1"
1515
base64 = "0.22"
16+
lingua = { git = "https://github.com/braintrustdata/lingua.git" }
1617
bon = "3"
1718
arc-swap = "1"
1819
async-trait = "0.1"

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod json_merge;
66
mod log_queue;
77
mod logger;
88
mod logs3;
9+
mod prompt;
910
mod span;
1011
mod span_components;
1112
mod stream;
@@ -21,12 +22,15 @@ pub use experiments::{
2122
GitMetadataSettings, MetricSummary, ProjectMetadata, RepoInfo, ScoreSummary,
2223
};
2324
pub use extractors::{extract_anthropic_usage, extract_openai_usage};
25+
pub use lingua::universal::{AssistantContent, TokenBudget, UserContent};
26+
pub use lingua::{Message, UniversalParams, UniversalRequest};
2427
pub use log_queue::LogQueueConfig;
2528
pub use logger::{
2629
BraintrustClient, BraintrustClientBuilder, LoginState, OrgInfo, DEFAULT_API_URL,
2730
DEFAULT_APP_URL,
2831
};
2932
pub use logs3::{Logs3BatchUploader, Logs3UploadResult};
33+
pub use prompt::{Prompt, PromptBuilder, PromptBuilderError};
3034
pub use span::{SpanBuilder, SpanHandle, SpanLog, SpanLogBuilder, SpanLogBuilderError};
3135
pub use span_components::SpanComponents;
3236
pub use stream::{

src/logger.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::experiments::api::{
1515
};
1616
use crate::experiments::{BaseExperimentInfo, ExperimentBuilder};
1717
use crate::log_queue::{LogQueue, LogQueueConfig};
18+
use crate::prompt::{PromptBuilder, PromptFetchRequest, PromptFetchResponse, PromptFetcher};
1819
use crate::span::SpanSubmitter;
1920
use crate::types::{ParentSpanInfo, SpanPayload};
2021

@@ -505,6 +506,13 @@ impl BraintrustClient {
505506
crate::span::SpanBuilder::new(submitter, token, org_id)
506507
}
507508

509+
/// Create a prompt builder with an explicit API token.
510+
///
511+
/// Use this if you already have the token and don't want to use the login state.
512+
pub fn prompt_builder_with_credentials(&self, token: impl Into<String>) -> PromptBuilder<Self> {
513+
PromptBuilder::new(Arc::new(self.clone()), token)
514+
}
515+
508516
/// Perform login synchronously.
509517
async fn perform_login(&self, api_key: &str, org_name: Option<&str>) -> Result<()> {
510518
let login_url = self
@@ -910,6 +918,64 @@ impl ExperimentComparisonFetcher for BraintrustClient {
910918
}
911919
}
912920

921+
#[async_trait::async_trait]
922+
impl PromptFetcher for BraintrustClient {
923+
async fn fetch_prompt(
924+
&self,
925+
token: &str,
926+
request: PromptFetchRequest,
927+
) -> Result<PromptFetchResponse> {
928+
let mut url = self
929+
.inner
930+
.api_url
931+
.join("v1/prompt")
932+
.map_err(|e| BraintrustError::InvalidConfig(e.to_string()))?;
933+
934+
{
935+
let mut query = url.query_pairs_mut();
936+
query.append_pair("slug", &request.slug);
937+
if let Some(project_id) = &request.project_id {
938+
query.append_pair("project_id", project_id);
939+
}
940+
if let Some(project_name) = &request.project_name {
941+
query.append_pair("project_name", project_name);
942+
}
943+
if let Some(version) = &request.version {
944+
query.append_pair("version", version);
945+
}
946+
if let Some(environment) = &request.environment {
947+
query.append_pair("environment", environment);
948+
}
949+
}
950+
951+
let response = self
952+
.inner
953+
.http_client
954+
.get(url)
955+
.bearer_auth(token)
956+
.send()
957+
.await
958+
.map_err(|e| BraintrustError::Network(e.to_string()))?;
959+
960+
if !response.status().is_success() {
961+
let status = response.status();
962+
let body = response.text().await.unwrap_or_default();
963+
return Err(BraintrustError::Api {
964+
status: status.as_u16(),
965+
message: body,
966+
});
967+
}
968+
969+
response
970+
.json::<PromptFetchResponse>()
971+
.await
972+
.map_err(|e| BraintrustError::Api {
973+
status: 200,
974+
message: format!("Failed to parse prompt response: {}", e),
975+
})
976+
}
977+
}
978+
913979
#[allow(dead_code)]
914980
#[derive(Debug, Default)]
915981
struct MutableSpanEvent {

0 commit comments

Comments
 (0)