Skip to content

Commit f909706

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

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
@@ -12,6 +12,7 @@ categories = ["api-bindings", "asynchronous", "development-tools"]
1212

1313
[dependencies]
1414
anyhow = "1"
15+
lingua = { git = "https://github.com/braintrustdata/lingua.git" }
1516
bon = "3"
1617
arc-swap = "1"
1718
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 stream;
1112
#[cfg(test)]
@@ -20,12 +21,15 @@ pub use experiments::{
2021
GitMetadataSettings, MetricSummary, ProjectMetadata, RepoInfo, ScoreSummary,
2122
};
2223
pub use extractors::{extract_anthropic_usage, extract_openai_usage};
24+
pub use lingua::universal::{AssistantContent, TokenBudget, UserContent};
25+
pub use lingua::{Message, UniversalParams, UniversalRequest};
2326
pub use log_queue::LogQueueConfig;
2427
pub use logger::{
2528
BraintrustClient, BraintrustClientBuilder, LoginState, OrgInfo, DEFAULT_API_URL,
2629
DEFAULT_APP_URL,
2730
};
2831
pub use logs3::{Logs3BatchUploader, Logs3UploadResult};
32+
pub use prompt::{Prompt, PromptBuilder, PromptBuilderError};
2933
pub use span::{SpanBuilder, SpanHandle, SpanLog, SpanLogBuilder, SpanLogBuilderError};
3034
pub use stream::{
3135
wrap_stream_with_span, BraintrustStream, ChatMessage, ChatMessageBuilder,

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)